Rust
Definitely not AhoβCorasick cool or genius, but does get ~11ms on both parts. Gains over the other Rust solves are partly a less naΓ―ve approach to towel checking and partly getting it to work with 0 String
allocations, which involves both an instructive lifetime annotation and the clever use of a niche standard library function.
Code
pub fn day19(input: &str) -> (u64, u64) {
fn recur_helper<'a>(pat: &'a str, towels: &[&str], map: &mut HashMap<&'a str, u64>) -> u64 {
let mut hits = 0;
let mut towel_subset = towels;
for l in 1..=pat.len() {
let test_pat = &pat[0..l];
match towel_subset.binary_search(&test_pat) {
Ok(idx) => {
if pat[l..].is_empty() {
return hits + 1;
} else if let Some(&v) = map.get(&pat[l..]) {
hits += v;
} else {
let v = recur_helper(&pat[l..], towels, map);
map.insert(&pat[l..], v);
hits += v;
}
towel_subset = &towel_subset[idx..];
let end = towel_subset.partition_point(|&x| x.starts_with(test_pat));
towel_subset = &towel_subset[..end];
if towel_subset.is_empty() {
return hits;
}
}
Err(idx) => {
towel_subset = &towel_subset[idx..];
let end = towel_subset.partition_point(|&x| x.starts_with(test_pat));
towel_subset = &towel_subset[..end];
if towel_subset.is_empty() {
return hits;
}
}
}
}
hits
}
let mut part1 = 0;
let mut part2 = 0;
let mut lines = input.lines();
let mut towels = lines.next().unwrap().split(", ").collect::<Vec<_>>();
towels.sort();
let mut map = HashMap::new();
for pat in lines.skip(1) {
let tmp = recur_helper(pat, &towels, &mut map);
part2 += tmp;
if tmp > 0 {
part1 += 1;
}
}
(part1, part2)
}