Advent Of Code 2020 Day 2: Character Counting And String Indexing
For the second day of the Advent of Code, the story problem was about password validation. The first part was to determine if a string fit a rule about the number of characters. Each line has the form:
<lower bound>-<upper bound> <character>: <string>
The rule is that a line was valid if the password string has from
lower bound
to upper bound
instances of character
in string
. A
naive implementation of this in Python might look something like this:
s = "aaaabcddefg"
counts = {}
for c in s:
if c not in counts:
counts[c] = 0
counts[c] += 1
print(counts)
# -> {'a': 4, 'b': 1, 'c': 1, 'd': 2, 'e': 1, 'f': 1, 'g': 1}
print(counts["a"])
# -> 4
Fortunately, this is something that comes up a lot (as many of the AoC
challenges do), and so there is a standard library class in Python
called Counter
that will do this for us:
from collections import Counter
s = "aaaabcddefg"
counts = Counter(s)
print(counts)
# -> Counter({'a': 4, 'd': 2, 'b': 1, 'c': 1, 'e': 1, 'f': 1, 'g': 1})
print(counts["a"])
# -> 4
Much cleaner! To actually perform the check, Python has nice syntax for expressing relationships like $$ a \le b \le c $$:
a = 1
b = 2
c = 3
print(a <= b <= c)
# -> True
print(a <= c <= b)
# -> False
Naturally, it goes the other way, too:
a = 1
b = 2
c = 3
print(c >= b >= a)
# -> True
print(a >= b >= c)
# -> False
This makes the check very simple:
password_valid = lower_bound <= counts[c] <= upper_bound
Everything else is just parsing the lines and keep a counter.
The second part was to index string
at lower bound
and upper
bound
and require that either one or the other of the indices was
character
, with the caveat that the indexing is one-based
(presumably to make your life just a little more difficult). Strings
in Python are arrays of characters:
s = "aaaabcddefg"
print(s[0])
# -> a
So the fundamental idea isn’t hard. The slightly tricky part is that
this is an exclusive or
(XOR
) operation. XOR
is true
iff
exactly one of the inputs is true. Here’s a truth table:
x | y | XOR |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
In Python, the XOR
operator is ^
, and it’s usually a bitwise
operation, defined for numeric types like int
. You can implement a
boolean XOR
like this:
cond1 != cond2
Where cond1
and cond2
are boolean expressions. For this problem,
the expression would look like this (assuming the lower bound is lb
,
the upper bound is ub
and the character is c
):
password_valid = (password[lb - 1] == c) != (password[ub - 1] == c)
This is the tricky bit of part 2, everything else is just iterating over the lines and keeping a counter.