diff --git a/bin/day2419.ml b/bin/day2419.ml index f5b73fb..543d520 100644 --- a/bin/day2419.ml +++ b/bin/day2419.ml @@ -7,59 +7,50 @@ let towels_of_strings = function (h, t) | _ -> failwith "towels_of_strings" +(** [towels_of_file fname] returns the list of towels and patterns from the file + [fname]. *) let towels_of_file fname = Aoc.strings_of_file fname |> towels_of_strings + +(** Memoizing hash table shared between parts 1 and 2. *) let memo = Hashtbl.create 1000 -let rec memoize_has_pattern pattern towels = - let pattern_len = String.length pattern in - let rec has_pattern = function - | [] -> false - | h :: t -> - let towel_len = String.length h in - if String.starts_with ~prefix:h pattern then - memoize_has_pattern - (String.sub pattern towel_len (pattern_len - towel_len)) - towels - || has_pattern t - else has_pattern t - in - match Hashtbl.find_opt memo pattern with +(** [memoize memo f value] returns the result of [f value]. The hashtable [memo] + is used to cache results, so repeated calls with the same [value] will not + call [f] again. *) +let memoize memo f value = + match Hashtbl.find_opt memo value with | Some x -> x | None -> - let x = if pattern_len = 0 then true else has_pattern towels in - Hashtbl.add memo pattern x; + let x = f value in + Hashtbl.add memo value x; x -let has_match towels pattern = memoize_has_pattern pattern towels -let memo2 = Hashtbl.create 1000 - -let rec memoize_count_matches pattern towels = +(** [count_hashes memo towels pattern] counts the number of ways of matching + [pattern] using [towels]. [memo] is a hashtable used for memoizing results. +*) +let rec count_matches memo towels pattern = let pattern_len = String.length pattern in - let rec has_pattern = function + let rec count_matched = function | [] -> 0 | h :: t -> let towel_len = String.length h in if String.starts_with ~prefix:h pattern then - memoize_count_matches + memoize memo + (count_matches memo towels) (String.sub pattern towel_len (pattern_len - towel_len)) - towels - + has_pattern t - else has_pattern t + + count_matched t + else count_matched t in - match Hashtbl.find_opt memo2 pattern with - | Some x -> x - | None -> - let x = if pattern_len = 0 then 1 else has_pattern towels in - Hashtbl.add memo2 pattern x; - x - -let count_matches towels pattern = memoize_count_matches pattern towels + if pattern_len = 0 then 1 else count_matched towels let part1 (towels, patterns) = - List.filter (has_match towels) patterns |> List.length + List.map (count_matches memo towels) patterns + |> List.filter (( > ) 0) + |> List.length let part2 (towels, patterns) = - List.map (count_matches towels) patterns |> List.fold_left ( + ) 0 + List.map (memoize memo (count_matches memo towels)) patterns + |> List.fold_left ( + ) 0 let _ = Aoc.main towels_of_file [ (string_of_int, part1); (string_of_int, part2) ]