# Advent Of Code Day 5 [spoilers]

I completely gave up on solving the Advent of Code problems as they went up, and I'm backfilling the days I missed, which is most of them.

Day 5 was the first time I really felt happy with my Haskell solution, because lists are a good way to solve this problem. We are given a long string of lower-case and upper-case letters, and asked to simulate a process where matched upper/lower pairs are removed. For example, "cBaAbc" becomes "cBbc" becomes "cc", but "bb" doesn't react further.

(Actually, all we're asked for is the length of the string after all the pairs have been removed, so it's not impossible that there might be a way to answer this without simulation.)

The key insight here is that any "cascade" of reactions takes place just before the place where a pair was removed, so we don't need to start over the search each time. (We also need to assume, or prove, that the order of elimination doesn't matter.) We can backtrack one position from our current position in the string, every time we eliminate a pair.

It doesn't seem very Haskell-ish to keep a position into a list, which would be horribly inefficient anyway. We can handle this by having a recursive function that keeps two lists, the unprocessed part of the string and the already-visited part of the string. The trick is to keep the latter in reverse order so that we can easily back up.

``````eliminatePolymers :: String -> String -> String
eliminatePolymers stack (x:[]) = reverse (x:stack)
eliminatePolymers [] (x:y:xs) =
if match x y
then eliminatePolymers [] xs
else eliminatePolymers (x:[]) (y:xs)
eliminatePolymers (a:b) (x:y:xs) =
if match x y
then eliminatePolymers b (a:xs)
else eliminatePolymers (x:(a:b)) (y:xs)
``````

This version worked by it seemed a little redundant (in fact I did copy-and-paste.) Going down the direction of a second conditional didn't seem right; I managed to shorten it a little bit using guards:

``````eliminatePolymers :: String -> String -> String
eliminatePolymers stack (x:[]) = reverse (x:stack)
eliminatePolymers [] (x:y:xs)
| match x y = eliminatePolymers [] xs
eliminatePolymers (p:ps) (x:y:xs)
| match x y = eliminatePolymers ps (p:xs)
eliminatePolymers ps (x:y:xs) = eliminatePolymers (x:ps) (y:xs)
``````

The `match` function is trivial:

``````match :: Char -> Char -> Bool
match x y =
( isUpper x && isLower y && (toLower x) == y ) ||
( isLower x && isUpper y && (toUpper x) == y )
``````

And then the solution to part 1 is just `(length (eliminatePolymers [] input)`.

Part 2 asks us to examine removing each of the 26 letter pairs and seeing which results in the shortest string after going through the process above. `filter` can remove the upper and lower pairs:

``````removeUnits x polymer = filter (\y -> toLower y /= x) polymer
``````

And instead of calculating the minimum explicitly I generated a report of all the lengths (which was useful for testing with the short example given.)

``````lengthAfterRemoval a polymer = length (eliminatePolymers [] (removeUnits a polymer))

reportExperiment a =
"Length after removing " ++ [a] ++ " = " ++ (show (lengthAfterRemoval a input))

letters = "abcdefghijklmnopqrstuvwxyz"

report = intercalate "\n" [ reportExperiment a | a <- letters ]
``````
Sort:

This post has been voted on by the SteemSTEM curation team and voting trail in collaboration with @curie.

If you appreciate the work we are doing then consider voting both projects for witness by selecting stem.witness and curie!

If you no longer want to receive notifications, reply to this comment with the word `STOP`