1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
5pub struct Day05<'a> {
6 lines: Vec<&'a [u8]>,
7}
8
9impl<'a> Day05<'a> {
10 pub fn new(input: &'a str, _: InputType) -> Result<Self, InputError> {
11 Ok(Self {
12 lines: parser::take_while1(u8::is_ascii_lowercase)
13 .error_msg("expected a-z")
14 .parse_lines(input)?,
15 })
16 }
17
18 #[must_use]
19 #[expect(clippy::eq_op)]
20 pub fn part1(&self) -> usize {
21 const VOWELS: u32 = (1 << (b'a' - b'a'))
22 | (1 << (b'e' - b'a'))
23 | (1 << (b'i' - b'a'))
24 | (1 << (b'o' - b'a'))
25 | (1 << (b'u' - b'a'));
26
27 self.lines
28 .iter()
29 .filter(|&&l| l.windows(2).any(|w| w[0] == w[1]))
31 .filter(|&&l| {
33 l.iter()
34 .filter(|&&b| VOWELS & (1 << (b - b'a')) != 0)
36 .count()
37 >= 3
38 })
39 .filter(|&&l| l.windows(2).all(|w| w != b"ab"))
41 .filter(|&&l| l.windows(2).all(|w| w != b"cd"))
42 .filter(|&&l| l.windows(2).all(|w| w != b"pq"))
43 .filter(|&&l| l.windows(2).all(|w| w != b"xy"))
44 .count()
45 }
46
47 #[must_use]
48 pub fn part2(&self) -> usize {
49 let mut pair_positions = [0u32; 729];
51 let mut pos = 0;
52
53 self.lines
54 .iter()
55 .filter(|&&l| l.windows(3).any(|w| w[0] == w[2]))
57 .filter(|&&l| {
59 let string_start = pos;
60 l.windows(2).any(|w| {
61 let pair = 26 * (w[0] - b'a') as usize + (w[1] - b'a') as usize;
62 if pair_positions[pair] > string_start {
63 if pair_positions[pair] < pos {
65 return true;
67 }
68 } else {
69 pair_positions[pair] = pos + 1;
71 }
72 pos += 1;
73 false
74 })
75 })
76 .count()
77 }
78}
79
80examples!(Day05<'_> -> (usize, usize) [
81 {input: "ugknbfddgicrmopn", part1: 1},
82 {input: "aaa", part1: 1, part2: 0},
83 {input: "aaaa", part1: 1, part2: 1},
84 {input: "jchzalrnumimnmhp", part1: 0},
85 {input: "haegwjzuvuyypxyu", part1: 0},
86 {input: "dvszwmarrgswjxmb", part1: 0},
87 {input: "qjhvhtzxzqqjkmpb", part2: 1},
88 {input: "xxyxx", part2: 1},
89 {input: "uurcxstgmygtbstg", part2: 0},
90 {input: "ieodomkazucvgmuy", part2: 0},
91]);