]> aoc.elinar.fr Git - aoc_2024/commitdiff
Day19 - part 2
authoralex <>
Thu, 19 Dec 2024 15:35:02 +0000 (16:35 +0100)
committeralex <>
Thu, 19 Dec 2024 15:35:02 +0000 (16:35 +0100)
src/day19.rs

index 710105d2edbeca3cbece734fab1743ba5e011bbc..172ba2455f86c2b95a24e315526d027d9862f902 100644 (file)
@@ -1,42 +1,91 @@
 use std::error::Error;
 use std::path::Path;
-use std::collections::HashSet;
+use std::collections::{HashSet, HashMap};
 
-fn is_valid_design(design: &str, patterns: &HashSet<&str>) -> bool {
+struct Puzzle<'a> {
+    patterns: HashSet::<&'a str>,
+    designs: Vec<&'a str>,
+    memo: HashMap<&'a str, u64>,
+    n_valid: u64,
+}
+
+impl<'a> Puzzle<'a> {
+    pub fn new(input: &'a str) -> Self {
+        let (patterns, designs) = input.split_once("\n\n").unwrap();
+        let patterns = patterns.split(", ").collect();
+        let designs = designs.lines().collect();
+        let memo = HashMap::new();
+        let n_valid = 0;
 
-    if design.is_empty() {
-        return true
+        Self { patterns, designs, memo, n_valid }
     }
 
-    for i in 0..design.len() {
-        if patterns.contains(&design[0..=i]) &&
-            is_valid_design(&design[(i+1)..design.len()], patterns)
-        {
+    fn is_valid_design(&self, design: &str) -> bool {
+        if design.is_empty() {
             return true
         }
+
+        for i in 0..design.len() {
+            if self.patterns.contains(&design[0..=i]) &&
+                self.is_valid_design(&design[(i+1)..design.len()])
+            {
+                return true;
+            }
+        }
+        false
     }
 
-    false
+    fn all_valid_design(&mut self, design: &'a str) -> bool {
+        if let Some(n) = self.memo.get(design) {
+            self.n_valid += n;
+            return true;
+        }
+        if design.is_empty() {
+            self.n_valid += 1;
+            return true
+        }
+
+        let mut n = 0;
+        let mut is_valid = false;
+        for i in 0..design.len() {
+            if self.patterns.contains(&design[0..=i]) &&
+                self.all_valid_design(&design[(i+1)..design.len()])
+            {
+                n += match self.memo.get(&design[(i+1)..design.len()]) {
+                    Some(m) => *m,
+                    None => 1
+                };
+                is_valid = true;
+            }
+        }
+
+        self.memo.insert(design, n);
+
+        is_valid
+    }
 }
 
 fn run_part1(input: &str) -> Result<u32, Box<dyn Error>> {
     println!("Running {} - part 1", get_day());
 
-    let (patterns, designs) = input.split_once("\n\n").unwrap();
-    let patterns: HashSet<&str> = patterns.split(", ")
-        .collect();
-
-    let res = designs.lines()
-        .filter(|d| is_valid_design(d, &patterns))
+    let puzzle = Puzzle::new(input);
+    let res = puzzle.designs.iter()
+        .filter(|d| puzzle.is_valid_design(d))
         .count() as u32;
 
     Ok(res)
 }
 
-fn run_part2(input: &str) -> Result<u32, Box<dyn Error>> {
+fn run_part2(input: &str) -> Result<u64, Box<dyn Error>> {
     println!("Running {} - part 2", get_day());
 
-    Ok(0)
+    let mut puzzle = Puzzle::new(input);
+    puzzle.designs.clone().iter()
+        .for_each(|d| { puzzle.all_valid_design(d); });
+
+    let res = puzzle.n_valid;
+
+    Ok(res)
 }
 
 pub fn run(input: &str) -> Result<(), Box<dyn Error>> {
@@ -99,6 +148,6 @@ abcdeea";
 
     #[test]
     fn test_part2() {
-        assert_eq!(0, run_part2(TEXT_INPUT).unwrap());
+        assert_eq!(16, run_part2(TEXT_INPUT).unwrap());
     }
 }