diff options
author | Alexandre Jesus <adbjesus@gmail.com> | 2025-01-06 20:15:56 +0000 |
---|---|---|
committer | Alexandre Jesus <adbjesus@gmail.com> | 2025-01-06 20:15:56 +0000 |
commit | efd904643f3564519dbcdabf375941630aa45e3a (patch) | |
tree | 89967645fecbebaab9b054409c0398eb62029d72 /src | |
parent | f02a1c01247475a848d9a1b5062c8f4f27fbef65 (diff) | |
download | aoc2024-master.tar.gz aoc2024-master.zip |
Diffstat (limited to 'src')
-rw-r--r-- | src/day19.exs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/day19.exs b/src/day19.exs new file mode 100644 index 0000000..9c9a1e2 --- /dev/null +++ b/src/day19.exs @@ -0,0 +1,104 @@ +defmodule Day19 do + defmodule Onsen do + @derive Inspect + @enforce_keys [:patterns, :designs] + defstruct @enforce_keys + + def from_data(data) do + [patterns, designs] = data + |> String.split("\n\n", trim: true) + + patterns = patterns + |> String.split(", ", trim: true) + + designs = designs + |> String.split("\n", trim: true) + + %Onsen{patterns: patterns, designs: designs} + end + + def count_feasible_designs(onsen) do + onsen.designs + |> Enum.filter(& feasible?(&1, onsen.patterns) |> elem(0)) + |> Enum.count() + end + + defp feasible?(design, patterns, vis \\ MapSet.new()) do + cond do + MapSet.member?(vis, design) -> + {false, vis} + design == "" -> + {true, vis} + true -> + for pat <- patterns, reduce: {false, MapSet.put(vis, design)} do + {acc, vis} -> + cond do + acc == true -> + {acc, vis} + String.starts_with?(design, pat) -> + design + |> String.split_at(String.length(pat)) + |> elem(1) + |> feasible?(patterns, vis) + true -> + {acc, vis} + end + end + end + end + + def count_feasible_ways(onsen) do + onsen.designs + |> Enum.map(& ways(&1, onsen.patterns) |> elem(0)) + |> Enum.sum() + end + + defp ways(design, patterns, vis \\ Map.new()) do + case Map.fetch(vis, design) do + {:ok, count} -> + {count, vis} + :error -> + if design == "" do + {1, vis} + else + for pat <- patterns, reduce: {0, vis} do + {acc, vis} -> + cond do + String.starts_with?(design, pat) -> + design + |> String.split_at(String.length(pat)) + |> elem(1) + |> ways(patterns, vis) + |> then(fn {acc2, vis} -> {acc + acc2, vis} end) + true -> + {acc, vis} + end + end + |> then(fn {acc, vis} -> {acc, Map.put(vis, design, acc)} end) + end + end + end + end + + def part1(data) do + data + |> Onsen.from_data() + |> Onsen.count_feasible_designs() + end + + def part2(data) do + data + |> Onsen.from_data() + |> Onsen.count_feasible_ways() + end +end + +data = IO.read(:stdio, :eof) + +{time1, ans1} = :timer.tc(fn -> Day19.part1(data) end) +IO.puts("Time : #{time1 / 1000000}") +IO.puts("Answer: #{ans1}") + +{time2, ans2} = :timer.tc(fn -> Day19.part2(data) end) +IO.puts("Time : #{time2 / 1000000}") +IO.puts("Answer: #{ans2}") |