From f51dc9c14514d7c04ce8196e6b80fbd8855e7fe6 Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Fri, 5 Dec 2025 04:29:55 -0800 Subject: [PATCH] day2: clever and fast solution --- src/day2.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/day2.rs b/src/day2.rs index 79dd47b..a9c2ae3 100644 --- a/src/day2.rs +++ b/src/day2.rs @@ -1,6 +1,7 @@ use aoc_runner_derive::{aoc, aoc_generator}; use itertools::Itertools; use misc::POW10; +use std::cmp::{max, min}; use std::ops::RangeInclusive; #[aoc_generator(day2)] @@ -88,15 +89,15 @@ fn part2_arithmetic_brute(input: &[RangeInclusive]) -> u64 { let n_digits = (product.ilog10() + 1) as usize; for n in 1..=n_digits / 2 { - let repetitions = n_digits / n; if !n_digits.is_multiple_of(n) { continue; } - let decade = POW10[n_digits - n]; + let repetitions = n_digits / n; + let decade = POW10[n_digits - n]; // get the power of 10 to split the leftmost n digits let lhs = product / decade; let remainder = product % decade; - // for each repetition we multiply by 10^(rep * n) + // for each repetition we multiply by 10^(rep * n) to add the appropriate zeros let expected_remainder = (0..repetitions - 1).map(|rep| lhs * POW10[rep * n]).sum(); if remainder == expected_remainder { @@ -139,6 +140,48 @@ fn part1_clever(input: &[RangeInclusive]) -> u64 { invalid_sum } +const fn n_digits(n: &u64) -> usize { + (n.ilog10() + 1) as usize +} + +fn generate_repeaters(r: &RangeInclusive, n: usize) -> Vec { + let mut invalids = Vec::new(); + for r in split_by_decade(r) { + let n_digits = n_digits(r.start()); + if !n_digits.is_multiple_of(n) || n_digits < 2 { + continue; + } + + let repetitions = n_digits / n; + let decade = POW10[n_digits - n]; + for lhs in (r.start() / decade)..=(r.end() / decade) { + let repeater = (0..repetitions) + .map(|rep| lhs * POW10[rep * n]) + .sum::(); + if r.contains(&repeater) { + invalids.push(repeater) + } + } + } + invalids +} + +fn split_by_decade(r: &RangeInclusive) -> impl Iterator> { + let (start, end) = (*r.start(), *r.end()); + (n_digits(r.start())..n_digits(r.end()) + 1) + .map(move |nd| RangeInclusive::new(max(start, POW10[nd - 1]), min(end, POW10[nd] - 1))) +} + +#[aoc(day2, part2, Clever)] +fn part2_clever(input: &[RangeInclusive]) -> u64 { + input + .iter() + .flat_map(|r| (1..=n_digits(r.end()) / 2).map(|nd| generate_repeaters(r, nd))) + .flatten() + .unique() + .sum() +} + #[cfg(test)] mod tests { use super::*; @@ -167,4 +210,8 @@ mod tests { fn part1_clever_example() { assert_eq!(part1_clever(&parse(EXAMPLE)), 1227775554); } + #[test] + fn part2_clever_example() { + assert_eq!(part2_clever(&parse(EXAMPLE)), 4174379265); + } }