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