AdventOfCode/y2015/src/days/d19.rs

111 lines
3.3 KiB
Rust

use std::collections::HashSet;
pub fn process_part1(input: &str) -> u32 {
let (replacements_str, molecule) = input.split_once("\n\n").unwrap();
let mut replacements = Vec::new();
replacements_str.lines().for_each(|line| {
let (from, to) = line.split_once(" => ").unwrap();
replacements.push((from, to));
});
let mut possible = HashSet::new();
for (from, to) in replacements {
molecule.match_indices(from).for_each(|(idx, _)| {
let mut new_possible = molecule.to_string();
new_possible.replace_range(idx..(idx + from.len()), to);
possible.insert(new_possible);
});
}
possible.len() as u32
}
// https://www.reddit.com/r/adventofcode/comments/3xflz8/comment/cy4etju/
// broken test
//First insight
//
//There are only two types of productions:
//
// e => XX and X => XX (X is not Rn, Y, or Ar)
//
// X => X Rn X Ar | X Rn X Y X Ar | X Rn X Y X Y X Ar
//
//Second insight
//
//You can think of Rn Y Ar as the characters ( , ):
//
//X => X(X) | X(X,X) | X(X,X,X)
//
//Whenever there are two adjacent "elements" in your "molecule", you apply the first production. This reduces your molecule length by 1 each time.
//
//And whenever you have T(T) T(T,T) or T(T,T,T) (T is a literal token such as "Mg", i.e. not a nonterminal like "TiTiCaCa"), you apply the second production. This reduces your molecule length by 3, 5, or 7.
//Third insight
//
//Repeatedly applying X => XX until you arrive at a single token takes count(tokens) - 1 steps:
//
//ABCDE => XCDE => XDE => XE => X
//count("ABCDE") = 5
//5 - 1 = 4 steps
//
//Applying X => X(X) is similar to X => XX, except you get the () for free. This can be expressed as count(tokens) - count("(" or ")") - 1.
//
//A(B(C(D(E)))) => A(B(C(X))) => A(B(X)) => A(X) => X
//count("A(B(C(D(E))))") = 13
//count("(((())))") = 8
//13 - 8 - 1 = 4 steps
//
//You can generalize to X => X(X,X) by noting that each , reduces the length by two (,X). The new formula is count(tokens) - count("(" or ")") - 2*count(",") - 1.
//
//A(B(C,D),E(F,G)) => A(B(C,D),X) => A(X,X) => X
//count("A(B(C,D),E(F,G))") = 16
//count("(()())") = 6
//count(",,,") = 3
//16 - 6 - 2*3 - 1 = 3 steps
//
//This final formula works for all of the production types (for X => XX, the (,) counts are zero by definition.)
pub fn process_part2(input: &str) -> u32 {
let (_replacements_str, molecule) = input.split_once("\n\n").unwrap();
// An element is always at least an uppercase character and possibly an additional lowercase
// character
let total_elements = molecule.chars().filter(|char| char.is_uppercase()).count();
let num_rn = molecule.matches("Rn").count();
let num_ar = molecule.matches("Ar").count();
let num_y = molecule.matches("Y").count();
(total_elements - (num_rn + num_ar) - 2 * num_y - 1) as u32
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT1: &str = "H => HO
H => OH
O => HH
e => H
e => O
HOH";
const INPUT2: &str = "H => HO
H => OH
O => HH
e => H
e => O
HOHOHO";
#[test]
fn part1() {
let result = process_part1(INPUT1);
assert_eq!(result, 4);
let result = process_part1(INPUT2);
assert_eq!(result, 7);
}
#[test]
fn part2() {
let result = process_part2(INPUT1);
assert_eq!(result, 3);
let result = process_part2(INPUT2);
assert_eq!(result, 6);
}
}