Skip to main content

aoc/year2020/
day01.rs

1//! # Report Repair
2//!
3//! The straightforward approach is to compare every possible pair of elements for part one and
4//! every possible triple for part two. This would have `O(n²)` and `O(n³)` time complexity respectively.
5//!
6//! We can do better with `O(n)` complexity for part one and `O(n²)` for part two.
7//!
8//! For part one we use an implicit hash table in an array, since values are constrained to between
9//! 0 and 2020 and each value is already perfectly hashed. For each entry we check the index
10//! at its value. If this is marked then we have seen the reciprocal `2020 - value` before
11//! so we can return the product. If not then we mark the reciprocal value in the array.
12//!
13//! Part two reuses the pair finding logic, finding the third element by stepping through the slice
14//! one by one and adjusting the target total. To reuse the array without reallocating
15//! (which is slow) we use a `round` value instead of `bool`.
16use crate::util::parse::*;
17
18pub fn parse(input: &str) -> Vec<usize> {
19    let mut numbers: Vec<usize> = input.iter_unsigned().collect();
20    // Assume that, after sorting, the solutions are closer to the middle of the vector than the
21    // extremes. It's not necessary for correctness but improves the average running time for
22    // most (all?) inputs.
23    numbers.sort_unstable();
24    numbers
25}
26
27pub fn part1(input: &[usize]) -> usize {
28    let mut hash = [0; 2020];
29    two_sum(input, 2020, &mut hash, 1).unwrap()
30}
31
32pub fn part2(input: &[usize]) -> usize {
33    let mut hash = [0; 2020];
34
35    (0..input.len() - 2)
36        .find_map(|i| {
37            let first = input[i];
38            let round = i + 1;
39            let target = 2020 - first;
40            two_sum(&input[round..], target, &mut hash, round).map(|product| first * product)
41        })
42        .unwrap()
43}
44
45fn two_sum(slice: &[usize], target: usize, hash: &mut [usize], round: usize) -> Option<usize> {
46    for &i in slice {
47        if i < target {
48            let complement = target - i;
49            if hash[i] == round {
50                return Some(i * complement);
51            }
52            hash[complement] = round;
53        }
54    }
55
56    None
57}