1
1
//! # Medicine for Rudolph
2
2
//!
3
- //! Part one is a brute force search and replace of every possibility.
3
+ //! Part one is a brute force search and replace of every possibility with two optimizations.
4
+ //! Replacements that add the same number of extra molecules are grouped together, as different
5
+ //! length strings can never match.
6
+ //!
7
+ //! Next replacement ranges are sorted into ascending order. Non-overlapping ranges can never match,
8
+ //! so checking for other equals string only needs to consider ranges that intersect.
4
9
//!
5
10
//! Part two uses the analysis from `askalski` provided on the
6
11
//! [Day 19 solution megathread](https://www.reddit.com/r/adventofcode/comments/3xflz8/day_19_solutions/).
@@ -15,29 +20,57 @@ pub fn parse(input: &str) -> Input<'_> {
15
20
16
21
pub fn part1 ( input : & Input < ' _ > ) -> usize {
17
22
let ( molecule, replacements) = input;
18
- let mut distinct = FastSet :: new ( ) ;
19
23
20
- for ( from, to) in replacements {
21
- for ( start, _) in molecule. match_indices ( from) {
22
- let size = molecule. len ( ) - from. len ( ) + to. len ( ) ;
23
- let end = start + from. len ( ) ;
24
+ let mut groups = FastMap :: new ( ) ;
25
+ let mut modified = Vec :: new ( ) ;
26
+ let mut result = 0 ;
24
27
25
- let mut string = String :: with_capacity ( size) ;
26
- string. push_str ( & molecule[ ..start] ) ;
27
- string. push_str ( to) ;
28
- string. push_str ( & molecule[ end..] ) ;
28
+ // Group replacements of the same size together.
29
+ for & ( from, to) in replacements {
30
+ let extra = to. len ( ) - from. len ( ) ;
31
+ groups. entry ( extra) . or_insert ( Vec :: new ( ) ) . push ( ( from, to) ) ;
32
+ }
29
33
30
- distinct. insert ( string) ;
34
+ for ( _, group) in groups {
35
+ // Build list of all possible modified strings.
36
+ for ( from, to) in group {
37
+ for ( start, _) in molecule. match_indices ( from) {
38
+ let end = start + from. len ( ) ;
39
+ modified. push ( ( start, end, to) ) ;
40
+ }
31
41
}
42
+
43
+ modified. sort_unstable_by_key ( |& ( start, ..) | start) ;
44
+
45
+ ' outer: for ( i, & ( start, end, to) ) in modified. iter ( ) . enumerate ( ) {
46
+ for & ( start2, _, to2) in & modified[ i + 1 ..] {
47
+ // Stop checking early once ranges no longer overlap.
48
+ if start2 >= start + to. len ( ) {
49
+ break ;
50
+ }
51
+
52
+ // Compare replaced sections for equality.
53
+ let first = to. bytes ( ) . chain ( molecule[ end..] . bytes ( ) ) ;
54
+ let second = molecule[ start..start2] . bytes ( ) . chain ( to2. bytes ( ) ) ;
55
+
56
+ if first. zip ( second) . all ( |( a, b) | a == b) {
57
+ continue ' outer;
58
+ }
59
+ }
60
+
61
+ result += 1 ;
62
+ }
63
+
64
+ modified. clear ( ) ;
32
65
}
33
66
34
- distinct . len ( )
67
+ result
35
68
}
36
69
37
70
pub fn part2 ( input : & Input < ' _ > ) -> usize {
38
71
let ( molecule, _) = input;
39
72
40
- let elements = molecule. chars ( ) . filter ( char :: is_ascii_uppercase) . count ( ) ;
73
+ let elements = molecule. bytes ( ) . filter ( u8 :: is_ascii_uppercase) . count ( ) ;
41
74
let rn = molecule. matches ( "Rn" ) . count ( ) ;
42
75
let ar = molecule. matches ( "Ar" ) . count ( ) ;
43
76
let y = molecule. matches ( 'Y' ) . count ( ) ;
0 commit comments