summaryrefslogtreecommitdiff
path: root/19-1.hs
blob: 7bda88ceb777827b3a76043f30f5f0c7a2d31d51 (plain)
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
import Control.Monad.Trans.State
import Data.Foldable (foldrM)
import Data.HashMap.Lazy as HM hiding (filter, mapMaybe, map, null)
import Data.List (nub)
import Data.Maybe (mapMaybe)

import Debug.Trace

type S = HashMap String [String]

-- Return Nothing if no match; unmatched remainder (can be []) if
-- match.
match :: String -> String -> Maybe String
match xs [] = Just xs
match (x:xs) (y:ys) | x == y    = match xs ys
                    | otherwise = Nothing
match [] _ = Nothing

matchAny :: [String] -> String -> State S [String]
matchAny yss xs = do
  s <- get
  case HM.lookup xs s of
    Nothing -> let r = mapMaybe (match xs) yss
               in put (HM.insert xs r s) >> return r
    Just x -> return x

check :: [String] -> String -> State S Bool
check yss xs = do
  zss <- nub <$> matchAny yss xs
  if [] `elem` zss
    then return True
    else anyM (check yss) zss

anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool
anyM p = foldrM f False
  where
    f a b = (b ||) <$> p a

parseTowels :: String -> [String]
parseTowels = map init . words

parse :: String -> ([String],[String])
parse s = let yss = filter (not . null) . drop 2 $ lines s
              xss = parseTowels . head $ lines s
          in (xss, yss)

main :: IO ()
main = do
  input <- getContents
  let (xss, yss) = parse input
  print . length . filter id $ evalState (mapM (check xss) yss) HM.empty