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.