11//! # Medicine for Rudolph
22//!
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.
49//!
510//! Part two uses the analysis from `askalski` provided on the
611//! [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<'_> {
1520
1621pub fn part1 ( input : & Input < ' _ > ) -> usize {
1722 let ( molecule, replacements) = input;
18- let mut distinct = FastSet :: new ( ) ;
1923
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 ;
2427
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+ }
2933
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+ }
3141 }
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 ( ) ;
3265 }
3366
34- distinct . len ( )
67+ result
3568}
3669
3770pub fn part2 ( input : & Input < ' _ > ) -> usize {
3871 let ( molecule, _) = input;
3972
40- let elements = molecule. chars ( ) . filter ( char :: is_ascii_uppercase) . count ( ) ;
73+ let elements = molecule. bytes ( ) . filter ( u8 :: is_ascii_uppercase) . count ( ) ;
4174 let rn = molecule. matches ( "Rn" ) . count ( ) ;
4275 let ar = molecule. matches ( "Ar" ) . count ( ) ;
4376 let y = molecule. matches ( 'Y' ) . count ( ) ;
0 commit comments