1use std::num::NonZeroU32;
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day19 {
7 part1: u64,
8 part2: u64,
9}
10
11#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
12enum Stripe {
13 #[default]
14 W,
15 U,
16 B,
17 R,
18 G,
19}
20
21#[derive(Clone, Debug, Default)]
22struct TrieNode {
23 child_offsets: [Option<NonZeroU32>; 5],
24 is_terminal: bool,
25}
26
27const MAX_PATTERN_LENGTH: usize = 8;
28const MAX_DESIGN_LENGTH: usize = 64;
29
30impl Day19 {
31 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
32 let letter = parser::byte_map!(
33 b'w' => Stripe::W,
34 b'u' => Stripe::U,
35 b'b' => Stripe::B,
36 b'r' => Stripe::R,
37 b'g' => Stripe::G,
38 );
39
40 let Some((patterns, designs)) = input
41 .split_once("\n\n")
42 .or_else(|| input.split_once("\r\n\r\n"))
43 else {
44 return Err(InputError::new(input, 0, "expected patterns and designs"));
45 };
46
47 let mut trie = vec![TrieNode::default()];
48 for item in letter
49 .repeat_arrayvec::<MAX_PATTERN_LENGTH, _>(parser::noop(), 1)
50 .with_suffix(", ".or(parser::eof()))
51 .parse_iterator(patterns)
52 {
53 let mut index = 0;
54 for &s in item?.iter() {
55 match trie[index].child_offsets[s as usize] {
56 None => {
57 trie.push(TrieNode::default());
58 trie[index].child_offsets[s as usize] =
59 Some(NonZeroU32::new((trie.len() - 1 - index) as u32).unwrap());
60 index = trie.len() - 1;
61 }
62 Some(offset) => index += offset.get() as usize,
63 }
64 }
65 trie[index].is_terminal = true;
66 }
67
68 let (mut part1, mut part2) = (0, 0);
69 let mut combinations = [0; MAX_DESIGN_LENGTH + 1];
70 combinations[0] = 1;
71 for item in letter
72 .repeat_arrayvec::<MAX_DESIGN_LENGTH, _>(parser::noop(), 1)
73 .with_suffix(parser::eol())
74 .parse_iterator(designs)
75 {
76 let design = item?;
77 for len in 1..=design.len() {
78 combinations[len] = 0;
79
80 let mut trie_index = 0;
81 for (i, &stripe) in design[design.len() - len..]
82 .iter()
83 .take(MAX_PATTERN_LENGTH)
84 .enumerate()
85 {
86 match trie[trie_index].child_offsets[stripe as usize] {
87 None => break,
88 Some(offset) => trie_index += offset.get() as usize,
89 }
90
91 combinations[len] +=
92 u64::from(trie[trie_index].is_terminal) * combinations[len - 1 - i];
93 }
94 }
95
96 let ways = combinations[design.len()];
97 part1 += if ways > 0 { 1 } else { 0 };
98 part2 += ways;
99 }
100
101 Ok(Self { part1, part2 })
102 }
103
104 #[must_use]
105 pub fn part1(&self) -> u64 {
106 self.part1
107 }
108
109 #[must_use]
110 pub fn part2(&self) -> u64 {
111 self.part2
112 }
113}
114
115examples!(Day19 -> (u64, u64) [
116 {file: "day19_example0.txt", part1: 6, part2: 16},
117]);