From b00e1fa769e1df7c497e2aa9c9545fb20ab81a63 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Sun, 22 Dec 2024 11:40:37 +0100 Subject: [PATCH] feat: day 19 --- README.md | 3 +- data/examples/19.txt | 10 ++++ src/bin/19.rs | 127 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 data/examples/19.txt create mode 100644 src/bin/19.rs diff --git a/README.md b/README.md index 5365520..15ab7d9 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | [Day 16](./src/bin/16.rs) | `-` | `-` | | [Day 17](./src/bin/17.rs) | `1.5ms` | `-` | | [Day 18](./src/bin/18.rs) | `2.1ms` | `5.0ms` | +| [Day 19](./src/bin/19.rs) | `21.1ms` | `707.2ms` | -**Total: 13275.60ms** +**Total: 14003.90ms** --- diff --git a/data/examples/19.txt b/data/examples/19.txt new file mode 100644 index 0000000..29648be --- /dev/null +++ b/data/examples/19.txt @@ -0,0 +1,10 @@ +r, wr, b, g, bwu, rb, gb, br + +brwrr +bggr +gbbr +rrbgbr +ubwu +bwurrg +brgr +bbrgwb diff --git a/src/bin/19.rs b/src/bin/19.rs new file mode 100644 index 0000000..1dcc588 --- /dev/null +++ b/src/bin/19.rs @@ -0,0 +1,127 @@ +advent_of_code::solution!(19); + +use std::collections::HashMap; + +fn is_possible(pat: String, towels: &Vec) -> bool { + if towels.contains(&pat) { + return true; + } + for towel in towels { + if pat.starts_with(towel) { + let sub_possible = is_possible((pat[towel.len()..]).to_owned(), towels); + if sub_possible { + return true; + } + } + } + + false +} + +#[derive(Debug)] +struct State { + towels: Vec, + simplified: Vec, + patterns: Vec, + possible_cache: HashMap, +} + +fn simplified_towels(towels: Vec) -> Vec { + let mut res = Vec::new(); + for t in &towels { + let remaining: Vec = towels.clone().into_iter().filter(|t2| t2 != t).collect(); + if t.len() == 1 || !is_possible(t.clone(), &remaining) { + res.push(t.clone()); + } + } + res +} + +impl TryFrom<&str> for State { + type Error = (); + fn try_from(input: &str) -> Result { + let mut input: Vec<&str> = input.lines().collect(); + if input.len() < 3 { + return Err(()); + } + let mut towels: Vec = input.remove(0).split(", ").map(String::from).collect(); + towels.sort_by_key(|t| (t.len(), t.clone())); + input.remove(0); + Ok(Self { + patterns: input.into_iter().map(|s| s.to_owned()).collect(), + simplified: simplified_towels(towels.clone()), + towels, + possible_cache: HashMap::new(), + }) + } +} + +impl State { + fn is_possible(&self, pat: String) -> bool { + is_possible(pat, &self.simplified) + } + + fn nb_possible(&mut self, pat: String) -> usize { + if let Some(cached) = self.possible_cache.get(&pat) { + return *cached; + } + if pat.is_empty() { + self.possible_cache.insert(pat, 1); + return 1; + } + let mut res = 0; + for towel in &self.towels.clone() { + if pat.starts_with(towel) { + res += self.nb_possible((pat[towel.len()..]).to_owned()); + } + } + self.possible_cache.insert(pat, res); + res + } + + fn get_possible_patterns_count(&self) -> usize { + self.patterns + .iter() + .filter(|p| self.is_possible(p.to_string())) + .count() + } + + fn get_nb_possible_configurations(&mut self) -> usize { + self.patterns + .clone() + .into_iter() + .map(|p| self.nb_possible(p.to_string())) + .sum::() + } +} + +pub fn part_one(input: &str) -> Option { + State::try_from(input) + .ok()? + .get_possible_patterns_count() + .into() +} + +pub fn part_two(input: &str) -> Option { + State::try_from(input) + .ok()? + .get_nb_possible_configurations() + .into() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(6)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(16)); + } +}