|
| 1 | +import Data.List (nub, intercalate) |
| 2 | +import Data.Map qualified as M |
| 3 | + |
| 4 | +-- The manually memoized (without using the State monad) variation of 12.hs. |
| 5 | +-- |
| 6 | +-- This builds up from step 1 (12.memo1.hs), and adds the actual map lookup and |
| 7 | +-- insertion to memoize the results. The final version (12.hs) just abstracts |
| 8 | +-- this handling under the State monad. |
| 9 | + |
| 10 | +main :: IO () |
| 11 | +main = interact $ (++ "\n") . show . p2 . parse |
| 12 | + |
| 13 | +parse :: String -> [(String, [Int])] |
| 14 | +parse = map line . lines |
| 15 | + where |
| 16 | + line l = case words l of |
| 17 | + [s, num] -> (s, map read $ words $ map comma num) |
| 18 | + comma c = if c == ',' then ' ' else c |
| 19 | + |
| 20 | +p1, p2 :: [(String, [Int])] -> Int |
| 21 | +p1 = sum . map ways |
| 22 | +p2 = p1 . unfold |
| 23 | + |
| 24 | +unfold :: [(String, [Int])] -> [(String, [Int])] |
| 25 | +unfold = map f |
| 26 | + where f (s, xs) = (intercalate "?" (replicate 5 s), concat (replicate 5 xs)) |
| 27 | + |
| 28 | +type Rx = (String, [Int]) |
| 29 | +type Memo = M.Map Rx Int |
| 30 | + |
| 31 | +ways :: Rx -> Int |
| 32 | +ways = snd . ways' memo M.empty |
| 33 | + where |
| 34 | + -- Uncomment this to see a version that doesn't do any memoization |
| 35 | + --memo m k = ways' memo m k |
| 36 | + -- This one does the lookup + insertion which serves as our memoization |
| 37 | + memo m k = case M.lookup k m of |
| 38 | + Just v -> (m, v) |
| 39 | + Nothing -> let (m', v) = ways' memo m k in (M.insert k v m', v) |
| 40 | + |
| 41 | +ways' :: (Memo -> Rx -> (Memo, Int)) -> Memo -> Rx -> (Memo, Int) |
| 42 | +ways' f m ([], []) = (m, 1) |
| 43 | +ways' f m ([], [x]) = (m, 0) |
| 44 | +ways' f m (s, []) = if none '#' s then (m, 1) else (m, 0) |
| 45 | +ways' f m (('.':rs), xs) = f m (rs, xs) |
| 46 | +ways' f m (('?':rs), xs) = let (m1, v1) = f m (rs, xs) |
| 47 | + (m2, v2) = f m1 (('#':rs), xs) |
| 48 | + in (m2, v1 + v2) |
| 49 | +ways' f m (s, (x:rx)) | length s >= x && none '.' (take x s) && notAfter x '#' s |
| 50 | + = f m ((drop (x + 1) s), rx) |
| 51 | +ways' _ m _ = (m, 0) |
| 52 | + |
| 53 | +notAfter :: Int -> Char -> String -> Bool |
| 54 | +notAfter x c s = none c (take 1 (drop x s)) |
| 55 | + |
| 56 | +only, none :: Char -> String -> Bool |
| 57 | +only c = all (== c) . nub |
| 58 | +none c = not . any (== c) . nub |
0 commit comments