@@ -5,102 +5,72 @@ use common::{solution, Answer};
5
5
solution ! ( "Linen Layout" , 19 ) ;
6
6
7
7
fn part_a ( input : & str ) -> Answer {
8
- let problem = Problem :: parse ( input) ;
9
- let mut sum = 0 ;
10
-
11
- for i in 0 ..problem. needed . len ( ) {
12
- if problem. possible ( i) {
13
- sum += 1 ;
14
- }
15
- }
16
-
17
- sum. into ( )
8
+ let problem = Onsen :: parse ( input) ;
9
+ problem. possible ( ) . into ( )
18
10
}
19
11
20
12
fn part_b ( input : & str ) -> Answer {
21
- let problem = Problem :: parse ( input) ;
22
- let mut sum = 0 ;
23
-
24
- for i in 0 ..problem. needed . len ( ) {
25
- sum += problem. ways ( i) ;
26
- }
27
-
28
- sum. into ( )
13
+ let problem = Onsen :: parse ( input) ;
14
+ problem. ways ( ) . into ( )
29
15
}
30
16
31
- struct Problem {
32
- sources : Vec < String > ,
33
- needed : Vec < String > ,
17
+ struct Onsen < ' a > {
18
+ segments : Vec < & ' a str > ,
19
+ towels : Vec < & ' a str > ,
34
20
}
35
21
36
- impl Problem {
37
- fn parse ( input : & str ) -> Self {
22
+ impl < ' a > Onsen < ' a > {
23
+ fn parse ( input : & ' a str ) -> Self {
38
24
let ( sources, needed) = input. split_once ( "\n \n " ) . unwrap ( ) ;
39
- let sources = sources. split ( ", " ) . map ( |x| x . to_owned ( ) ) . collect ( ) ;
40
- let needed = needed. lines ( ) . map ( |x| x . to_owned ( ) ) . collect ( ) ;
25
+ let segments = sources. split ( ", " ) . collect ( ) ;
26
+ let towels = needed. lines ( ) . collect ( ) ;
41
27
42
- Self { sources , needed }
28
+ Self { segments , towels }
43
29
}
44
30
45
- fn possible ( & self , design : usize ) -> bool {
46
- fn _inner < ' a > (
47
- memo : & mut HashMap < & ' a str , bool > ,
48
- expected : & ' a str ,
49
- sources : & [ String ] ,
50
- ) -> bool {
51
- if let Some ( & cache) = memo. get ( expected) {
52
- return cache;
53
- }
54
-
55
- if expected. len ( ) == 0 {
56
- memo. insert ( expected, true ) ;
57
- return true ;
58
- }
59
-
60
- for source in sources {
61
- if expected. len ( ) >= source. len ( )
62
- && expected. starts_with ( source)
63
- && _inner ( memo, & expected[ source. len ( ) ..] , & sources)
64
- {
65
- memo. insert ( expected, true ) ;
66
- return true ;
67
- }
68
- }
69
-
70
- memo. insert ( expected, false ) ;
71
- false
72
- }
31
+ /// Returns the number of possible towel designs by counting all the towels
32
+ /// that can be made a non-zero number of ways.
33
+ fn possible ( & self ) -> usize {
34
+ self . towels
35
+ . iter ( )
36
+ . filter ( |x| count_ways ( & mut HashMap :: new ( ) , x, & self . segments ) != 0 )
37
+ . count ( )
38
+ }
73
39
74
- _inner ( & mut HashMap :: new ( ) , & self . needed [ design] , & self . sources )
40
+ /// Here we just sum up the number of ways each towel can be made.
41
+ fn ways ( & self ) -> u64 {
42
+ self . towels
43
+ . iter ( )
44
+ . map ( |x| count_ways ( & mut HashMap :: new ( ) , x, & self . segments ) )
45
+ . sum ( )
75
46
}
47
+ }
76
48
77
- fn ways ( & self , design : usize ) -> u64 {
78
- fn _inner < ' a > (
79
- memo : & mut HashMap < & ' a str , u64 > ,
80
- expected : & ' a str ,
81
- sources : & [ String ] ,
82
- ) -> u64 {
83
- if let Some ( & cache) = memo. get ( expected) {
84
- return cache;
85
- }
86
-
87
- if expected. len ( ) == 0 {
88
- return 1 ;
89
- }
90
-
91
- let mut ways = 0 ;
92
- for source in sources {
93
- if expected. len ( ) >= source. len ( ) && expected. starts_with ( source) {
94
- ways += _inner ( memo, & expected[ source. len ( ) ..] , & sources) ;
95
- }
96
- }
97
-
98
- memo. insert ( expected, ways) ;
99
- ways
100
- }
49
+ fn count_ways < ' a > ( memo : & mut HashMap < & ' a str , u64 > , expected : & ' a str , sources : & [ & ' a str ] ) -> u64 {
50
+ if let Some ( & cache) = memo. get ( expected) {
51
+ return cache;
52
+ }
101
53
102
- _inner ( & mut HashMap :: new ( ) , & self . needed [ design] , & self . sources )
54
+ // If there is no more towel to find designs for, we have found one way to
55
+ // make the towel.
56
+ if expected. is_empty ( ) {
57
+ return 1 ;
103
58
}
59
+
60
+ // Otherwise, we will sum up the number of ways the towel can be made from
61
+ // adding each of the available segments to the current towel, but only the
62
+ // ones that match the current pattern.
63
+ let mut ways = 0 ;
64
+ for source in sources {
65
+ if expected. len ( ) >= source. len ( ) && expected. starts_with ( source) {
66
+ ways += count_ways ( memo, & expected[ source. len ( ) ..] , sources) ;
67
+ }
68
+ }
69
+
70
+ // Memoization!!! This is what allows us to avoid evaluating huge segments
71
+ // of the tree and get good performance.
72
+ memo. insert ( expected, ways) ;
73
+ ways
104
74
}
105
75
106
76
#[ cfg( test) ]
0 commit comments