Skip to content

Commit 04fd360

Browse files
committed
[2024] Cleanup day 19
1 parent 01ad458 commit 04fd360

File tree

1 file changed

+50
-80
lines changed

1 file changed

+50
-80
lines changed

aoc_2024/src/day_19.rs

Lines changed: 50 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,102 +5,72 @@ use common::{solution, Answer};
55
solution!("Linen Layout", 19);
66

77
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()
1810
}
1911

2012
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()
2915
}
3016

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>,
3420
}
3521

36-
impl Problem {
37-
fn parse(input: &str) -> Self {
22+
impl<'a> Onsen<'a> {
23+
fn parse(input: &'a str) -> Self {
3824
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();
4127

42-
Self { sources, needed }
28+
Self { segments, towels }
4329
}
4430

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+
}
7339

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()
7546
}
47+
}
7648

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+
}
10153

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;
10358
}
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
10474
}
10575

10676
#[cfg(test)]

0 commit comments

Comments
 (0)