Advent Of Code Day 5 [spoilers]

in adventofcode •  6 years ago  (edited)

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 ]

Full solution: https://github.com/mgritter/aoc2018/blob/master/day5/day5.hs

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  




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!

For additional information please join us on the SteemSTEM discord and to get to know the rest of the community!

Congratulations @markgritter! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You received more than 10000 upvotes. Your next target is to reach 15000 upvotes.

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

Christmas Challenge - The party continues

Support SteemitBoard's project! Vote for its witness and get one more award!