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>> {
#[test]
fn test_part2() {
- assert_eq!(0, run_part2(TEXT_INPUT).unwrap());
+ assert_eq!(16, run_part2(TEXT_INPUT).unwrap());
}
}