-
Notifications
You must be signed in to change notification settings - Fork 9
/
day13.rs
132 lines (120 loc) · 4.56 KB
/
day13.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! # Distress Signal
//!
//! One possible approach is to parse the input into a tree, then compare recursively node
//! by node. We're going to use a much faster and simpler approach by noting an observation about
//! the input data. If the sequence `10` is replaced by any single character greater than `9` then
//! we can compare the 2 packets *lexigraphically*. We'll replace all occurences of `10` with `A`
//! then compare packets character by character.
//!
//! The rules to compare 2 packets become:
//! * If both characters are the same then it's a draw, move onto the next character in each packet.
//! * If the first packet is `]` and the second packet anything else, then the first list is shorter
//! so the packets are in order.
//! * Conversely if the second packet is `]` and the first packet anything else, the packets are not
//! in order.
//! * If the first packet is an opening `[` and the second character anything else, then we're
//! comparing a number with a list, so *push* the second character back onto the list to check
//! again along with a closing `]` character.
//! * Do a similar push if the second character is an opening `[` and the first anything else.
//! * Finally compare the 2 characters by value. Since we've already covered the equal case, one
//! is guaranteed to be greater or less than the other.
struct Packet<'a> {
slice: &'a [u8],
index: usize,
extra: Vec<u8>,
}
impl Packet<'_> {
fn new(str: &str) -> Packet<'_> {
Packet { slice: str.as_bytes(), index: 0, extra: Vec::new() }
}
}
pub fn parse(input: &str) -> Vec<&str> {
input.lines().filter(|line| !line.is_empty()).collect()
}
/// Count adjacent pairs of packets that are in order.
pub fn part1(input: &[&str]) -> usize {
input
.chunks_exact(2)
.enumerate()
.map(|(i, chunk)| {
let ordered = compare(chunk[0], chunk[1]);
if ordered {
i + 1
} else {
0
}
})
.sum()
}
/// Find the position of `[[2]]` and `[[6]]` in linear `O(n)` time.
///
/// One approach would be to insert `[[2]]` and `[[6]]` into the list, sort in `O(nlogn)` time,
/// then find the indices of the 2 values in `O(n)` time.
///
/// A much faster approach is to iterate over the list, comparing each packet first with `[[2]]`.
/// If the packets are in order, then increment the positions of *both* `[[2]]` and `[[6]]`,
/// since `[[2]]` is less than `[[6]]`.
///
/// If the packet and `[[2]]` are not in order, then also check against `[[6]]`, incrementing only
/// the second index if the 2 packets are in order.
///
/// This obtains the relative indices of `[[2]]` and `[[6]]` efficiently in fewer than `2n` comparisons.
pub fn part2(input: &[&str]) -> u32 {
let mut first = 1;
let mut second = 2;
for packet in input {
if compare(packet, "[[2]]") {
first += 1;
second += 1;
} else if compare(packet, "[[6]]") {
second += 1;
}
}
first * second
}
/// Compare 2 packets using the rules listed in the module description.
///
/// It's faster to use 2 temporary `vec`s to store extra characters, rather than copy each
/// packet into a mutable [`VecDeque`]. We use the [`or_else`] method on [`Option`] to check
/// in the temporary `vec` for available characters first.
///
/// [`VecDeque`]: std::collections::VecDeque
/// [`or_else`]: Option::or_else
fn compare(left: &str, right: &str) -> bool {
let mut left = Packet::new(left);
let mut right = Packet::new(right);
while let (Some(a), Some(b)) = (left.next(), right.next()) {
match (a, b) {
(a, b) if a == b => (),
(b']', _) => return true,
(_, b']') => return false,
(b'[', b) => {
right.extra.push(b']');
right.extra.push(b);
}
(a, b'[') => {
left.extra.push(b']');
left.extra.push(a);
}
(a, b) => return a < b,
}
}
unreachable!();
}
impl Iterator for Packet<'_> {
type Item = u8;
// Rely on the fact that all input is valid to avoid bounds checks
fn next(&mut self) -> Option<Self::Item> {
self.extra.pop().or_else(|| {
let (index, slice) = (self.index, self.slice);
// Replace occurences of "10" with "A"
if slice[index] == b'1' && slice[index + 1] == b'0' {
self.index += 2;
Some(b'A')
} else {
self.index += 1;
Some(slice[index])
}
})
}
}