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 2 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 input.lines().map(|line| line.unsigned()).collect()
20}
21
22pub fn part1(input: &[usize]) -> usize {
23 let mut hash = [0; 2020];
24 two_sum(input, 2020, &mut hash, 1).unwrap()
25}
26
27pub fn part2(input: &[usize]) -> usize {
28 let mut hash = [0; 2020];
29
30 for i in 0..(input.len() - 2) {
31 let first = input[i];
32 let round = i + 1;
33 let slice = &input[round..];
34 let target = 2020 - first;
35
36 if let Some(product) = two_sum(slice, target, &mut hash, round) {
37 return first * product;
38 }
39 }
40 unreachable!()
41}
42
43fn two_sum(slice: &[usize], target: usize, hash: &mut [usize], round: usize) -> Option<usize> {
44 for &i in slice {
45 if i < target {
46 if hash[i] == round {
47 return Some(i * (target - i));
48 }
49 hash[target - i] = round;
50 }
51 }
52
53 None
54}