use std::collections::{HashSet, VecDeque}; use aoc_runner_derive::{aoc, aoc_generator}; type Transformation = (String, String); #[aoc_generator(day19)] fn parse(input: &str) -> (Vec, String) { let (a, b) = input.split_once("\n\n").unwrap(); let t = a .lines() .map(|line| { let (lhs, rhs) = line.split_once(" => ").unwrap(); (lhs.to_string(), rhs.to_string()) }) .collect(); (t, b.to_string()) } #[aoc(day19, part1)] fn part1((transformations, start): &(Vec, String)) -> usize { let all = possibilities(transformations, start); all.len() } fn possibilities(transformations: &[Transformation], start: &str) -> HashSet { let all: HashSet = transformations .iter() .flat_map(|(from, to)| { let start_indexes = starts(start, from); start_indexes.into_iter().map(|si| { let mut s = start.to_string(); s.replace_range(si..si + from.len(), to); s }) }) .collect(); all } fn starts(a: &str, pattern: &str) -> Vec { let mut start = 0; let mut result = vec![]; while let Some(index) = a[start..].find(pattern) { result.push(start + index); start += index + 1; } result } #[aoc(day19, part2)] fn part2((transformations, target): &(Vec, String)) -> usize { search("e", transformations, target) } fn search(base: &str, transformations: &[Transformation], target: &str) -> usize { let mut seen = HashSet::new(); seen.insert(base.to_string()); let mut queue = VecDeque::new(); queue.push_back((base.to_string(), 0)); while let Some((from, depth)) = queue.pop_front() { for possibility in possibilities(transformations, &from) { if possibility == target { return depth + 1; } if seen.insert(possibility.clone()) { queue.push_back((possibility, depth + 1)); } } } unreachable!("No solution was found"); } #[cfg(test)] mod test { #[test] fn part1() { let input = "H => HO H => OH O => HH HOH"; assert_eq!(super::part1(&super::parse(input)), 4); } #[test] fn part2a() { let input = "e => H e => O H => HO H => OH O => HH HOH"; assert_eq!(super::part2(&super::parse(input)), 3); } #[test] fn part2b() { let input = "e => H e => O H => HO H => OH O => HH HOHOHO"; assert_eq!(super::part2(&super::parse(input)), 6); } }