aoc/year2022/
day06.rs

1//! # Tuning Trouble
2//!
3//! One solution to this problem is to use the [`windows`] method to slide over groups of the desired
4//! size, then construct a [`HashSet`] from the characters. If the [`HashSet`] is the same size
5//! as the window then we know that all characters are unique, as sets contain no duplicate elements.
6//!
7//! We'll use a faster approach that minimizes the work needed. Instead of creating a set for each
8//! window, we'll maintain the last position seen of each character. As we advance character by
9//! character we lookup the previous position. If this is within the packet size, then we advance
10//! the start of the packet to exclude that character. Once the packet has reached the desired
11//! size then we return the current index.
12//!
13//! [`windows`]: slice::windows
14//! [`HashSet`]: std::collections::HashSet
15
16/// Return the input directly.
17pub fn parse(input: &str) -> &str {
18    input
19}
20
21/// Find the first unique set of size 4
22pub fn part1(input: &str) -> usize {
23    find(input, 4)
24}
25
26/// Find the first unique set of size 14
27pub fn part2(input: &str) -> usize {
28    find(input, 14)
29}
30
31/// The cardinality of the input is only 26 so a fixed size array can store the last position
32/// of each character.
33fn find(input: &str, marker: usize) -> usize {
34    let mut start = 0;
35    let mut seen = [0; 26];
36
37    for (i, b) in input.bytes().enumerate() {
38        // Use the character as an index into the array.
39        let index = (b - b'a') as usize;
40        let previous = seen[index];
41        // Positions are 1-based.
42        seen[index] = i + 1;
43
44        // There's a duplicate so advance the start of the window one character past it.
45        if previous > start {
46            start = previous;
47        }
48        // We've reached the desired packet size with no duplicates so finish.
49        if i + 1 - start == marker {
50            return i + 1;
51        }
52    }
53
54    unreachable!()
55}