## DEV Community 👩‍💻👨‍💻 is a community of 967,911 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Jules

Posted on • Updated on

# Advent of Code 2015 - Day 5

Day 5 is one of those puzzles that has you processing strings as efficiently as you can manage. I love Python's ability to slice strings, it's a real strength of the language. Here's Part 1 of the puzzle:

So there are three things we need to do to decide whether a string is 'nice':

1. Check whether it has three or more vowels
2. Check whether it has a doubled letter
3. Ensure it doesn't contain the 'forbidden' strings

## 1. Summing vowels

In Python, a string is essentially a list of characters, and lists are iterable. That's why we can write code like `for c in 'hello':`. Iterables can be used in the `map()` function, with each item having a function applied to it, for example `str.count()` as in my example below:

``````def count_vowels(s):
return sum(map(s.count, "aeiou"))
``````

The return value of `map()` is also an iterable, which means it can be sent to the `sum()` function. This gives us a Pythonic (finally!) way of counting the vowels in a string.

## 2. Doubled letters

I wrote this part as a simple look through the string, with a two character sliding window. I saw lots of solutions in the megathread that used regular expressions to find doubled letters, such as `re.search(r'([a-z])\1', s)` and `re.search(r'(.)\1', s)`. All this confirmed for me is that regular expressions are another topic to go on my 'learn more about Python' list.

``````def check_double(s):
for i in range(len(s)-1):
if s[i] == s[i+1]:
return True
return False
``````

## 3. Forbidden pairs

I eventually found regular expression approaches to this test too (e.g. `re.search(r'ab|cd|pq|xy', s)`), but used a loop to code this test:

``````def check_forbidden(s):
for forbidden in ['ab', 'cd', 'pq', 'xy']:
if forbidden in s:
return False
return True
``````

Of course, in hindsight I could have written the code in exactly the same way as I checked for vowels:

``````def check_forbidden(s):
return sum(map(s.count, ['ab', 'cd', 'pq', 'xy'])) == 0
``````

Either way works, and combining the three tests I was able to move on to Part 2:

It's unusual for the puzzles to change so abruptly, but here we are. Now strings only have to pass two tests, but they are slightly more complicated than those in Part 1:

1. Check for letter pairs that appear twice without overlapping
2. Check for triplets where the first and last letter is the same (regardless of what the middle character is)

## 1. Letter pairs that appear twice

Really wish I was more familiar with regular expressions! I could have used something like `re.search(r'(.)(.).*\1\2', s)` as many cleverer Pythonistas did, but ended up writing the following:

``````def check_duplicates(s):
for i in range(len(s)-1):
check = s[i:i+2]
if check in s[i+2:]:
return True
return False
``````

This uses a sliding window to consider two characters at a time, and then look in the rest of the string to see if it appears again. So for the string `mqzxvvskslbxvyjt` we look for:

`````` [mq] in zxvvskslbxvyjt
m [qz] in xvvskslbxvyjt
mq [zx] in vvskslbxvyjt
mqz [xv] in vskslbxvyjt, which is a hit!
``````

## 2. Symmetrical triplets

Another sliding window, this time with three characters in the window:

``````def check_triples(s):
for i in range(len(s)-2):
if s[i] == s[i+2]:
return True
return False
``````

## Golfing

Inevitably there are some one-liners in the megathread. Here's both parts solved in a line of code each, in a comment by Reddit user Stactic:

``````print len([w for w in open('Day5Words.txt') if (re.search(r'([aeiou].*){3,}',w) and re.search(r'(.)\1', w) and not re.search(r'ab|cd|pq|xy', w))])

print len([w for w in open('Day5Words.txt') if (re.search(r'(..).*\1', w) and re.search(r'(.).\1', w))])
``````

Unlike a lot of golfing code, this remains pretty readable, even to a regex novice like me!

On to Day 6, and some flashing lights!