aoc/year2018/
day05.rs

1//! # Alchemical Reduction
2//!
3//! ## Part One
4//!
5//! This problem is similar to checking if a parentheses expression is balanced or not.
6//! We use a similar approach, maintaining a stack of unreacted polymer units. Each unit from the
7//! polymer is compared to the head of the stack using bitwise logic. Lowercase and uppercase ASCII
8//! codes for the same lettter are always are 32 apart, which can be checked very quickly using
9//! bitwise XOR. For example:
10//!
11//! ```none
12//!         A = 65 = 01000001
13//!         a = 97 = 01100001
14//!     A ^ a = 32 = 00100000
15//! ```
16//!
17//! If two units are the same type but opposite polarity then they are popped from the stack.
18//!
19//! ## Part Two
20//!
21//! An important optimization is to use the already reacted polymer from part one. This is
22//! approximately 20% of the size of the raw input. Then this smaller polymer is filtered
23//! further for each of the 26 kinds of unit.
24pub fn parse(input: &str) -> Vec<u8> {
25    collapse(input.trim().bytes())
26}
27
28pub fn part1(input: &[u8]) -> usize {
29    input.len()
30}
31
32pub fn part2(input: &[u8]) -> usize {
33    (b'a'..=b'z')
34        .map(|kind| collapse(input.iter().copied().filter(|&b| b | 32 != kind)).len())
35        .min()
36        .unwrap()
37}
38
39fn collapse(polymer: impl Iterator<Item = u8>) -> Vec<u8> {
40    // It's faster to keep the head of the stack in a dedicated variable. 0 is used as a special
41    // sentinel kind to indicate an empty stack as it will never match with any unit kind.
42    let mut head = 0;
43    let mut stack = Vec::with_capacity(10_000);
44
45    for unit in polymer {
46        // Uppercase and lowercase ASCII are always 32 apart.
47        if head ^ unit == 32 {
48            // The head reacts with the unit to annihilate each other so replace with the next unit
49            // from the stack.
50            head = stack.pop().unwrap_or(0);
51        } else {
52            // Don't push sentinel values.
53            if head != 0 {
54                stack.push(head);
55            }
56            head = unit;
57        }
58    }
59
60    if head != 0 {
61        stack.push(head);
62    }
63
64    stack
65}