Skip to content

Commit

Permalink
Optimize 2024 day 19
Browse files Browse the repository at this point in the history
Use a lookup table for parsing and replace the recursive function with working backwards through each design, calculating the number of combinations at each length
  • Loading branch information
ictrobot committed Dec 19, 2024
1 parent 43af32c commit 8d992a9
Showing 1 changed file with 35 additions and 35 deletions.
70 changes: 35 additions & 35 deletions crates/year2024/src/day19.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,23 @@ struct TrieNode {
is_terminal: bool,
}

const LETTER_LOOKUP: [Option<Stripe>; 256] = {
let mut x = [None; 256];
x[b'w' as usize] = Some(Stripe::W);
x[b'u' as usize] = Some(Stripe::U);
x[b'b' as usize] = Some(Stripe::B);
x[b'r' as usize] = Some(Stripe::R);
x[b'g' as usize] = Some(Stripe::G);
x
};

const MAX_PATTERN_LENGTH: usize = 8;
const MAX_DESIGN_LENGTH: usize = 64;

impl Day19 {
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
let letter = parser::literal_map!(
"w" => Stripe::W,
"u" => Stripe::U,
"b" => Stripe::B,
"r" => Stripe::R,
"g" => Stripe::G,
);
let letter = parser::byte()
.map_res(|b| LETTER_LOOKUP[b as usize].ok_or("expected 'w', 'u', 'b', 'r' or 'g'"));

let Some((patterns, designs)) = input.split_once("\n\n") else {
return Err(InputError::new(input, 0, "expected patterns and designs"));
Expand Down Expand Up @@ -63,44 +68,39 @@ impl Day19 {
}

let (mut part1, mut part2) = (0, 0);
let mut cache = [None; MAX_DESIGN_LENGTH];
let mut combinations = [0; MAX_DESIGN_LENGTH + 1];
combinations[0] = 1;
for item in letter
.repeat_arrayvec::<MAX_DESIGN_LENGTH, _>(parser::noop(), 1)
.with_suffix(parser::eol())
.parse_iterator(designs)
{
cache.fill(None);
let ways = Self::ways(&item?, &trie, &mut cache);
part1 += if ways > 0 { 1 } else { 0 };
part2 += ways;
}

Ok(Self { part1, part2 })
}

fn ways(design: &[Stripe], trie: &[TrieNode], cache: &mut [Option<u64>]) -> u64 {
if design.is_empty() {
return 1;
}
if let Some(result) = cache[design.len() - 1] {
return result;
}
let design = item?;
for len in 1..=design.len() {
combinations[len] = 0;

let mut trie_index = 0;
for (i, &stripe) in design[design.len() - len..]
.iter()
.take(MAX_PATTERN_LENGTH)
.enumerate()
{
match trie[trie_index].child_offsets[stripe as usize] {
None => break,
Some(offset) => trie_index += offset.get() as usize,
}

let mut total = 0;
let mut trie_index = 0;
for (i, &stripe) in design.iter().take(MAX_PATTERN_LENGTH).enumerate() {
match trie[trie_index].child_offsets[stripe as usize] {
None => break,
Some(offset) => trie_index += offset.get() as usize,
combinations[len] +=
u64::from(trie[trie_index].is_terminal) * combinations[len - 1 - i];
}
}

if trie[trie_index].is_terminal {
total += Self::ways(&design[i + 1..], trie, cache);
}
let ways = combinations[design.len()];
part1 += if ways > 0 { 1 } else { 0 };
part2 += ways;
}

cache[design.len() - 1] = Some(total);
total
Ok(Self { part1, part2 })
}

#[must_use]
Expand Down

0 comments on commit 8d992a9

Please sign in to comment.