-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9f684be
commit 7f442ce
Showing
7 changed files
with
141 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
//! # Linen Layout | ||
//! | ||
//! Solves both parts simultaneously. Part one is the number of designs with non-zero possible | ||
//! combinations. | ||
//! | ||
//! An elegant approach to check if the design starts with any towel is to first build a | ||
//! [trie](https://en.wikipedia.org/wiki/Trie). Each node in the trie stores a `bool` indicating | ||
//! if it's a valid towel and links to the next node for each possible character. | ||
//! | ||
//! The string are lower case ASCII letters meaning the cardinality is only 26, allowing us to use | ||
//! a fixed size array instead of a set. Additionally we store the Trie in a flat `vec`. This is | ||
//! simpler and faster than creating objects on the heap using [`Box`]. | ||
type Input = (u64, u64); | ||
|
||
pub fn parse(input: &str) -> Input { | ||
let (prefix, suffix) = input.split_once("\n\n").unwrap(); | ||
|
||
// Build Trie from all towels. | ||
let mut trie = Vec::with_capacity(1_000); | ||
trie.push(Node::new()); | ||
|
||
for towel in prefix.split(", ") { | ||
let mut i = 0; | ||
|
||
for j in towel.bytes().map(to_index) { | ||
if trie[i].next[j] == 0 { | ||
// This is a new prefix, so update the index to point to it then push new node. | ||
trie[i].next[j] = trie.len(); | ||
i = trie.len(); | ||
trie.push(Node::new()); | ||
} else { | ||
// Follow existing prefix. | ||
i = trie[i].next[j]; | ||
} | ||
} | ||
|
||
trie[i].towel = true; | ||
} | ||
|
||
let mut part_one = 0; | ||
let mut part_two = 0; | ||
let mut ways = Vec::with_capacity(100); | ||
|
||
for design in suffix.lines().map(str::as_bytes) { | ||
let size = design.len(); | ||
|
||
// Reset state. | ||
ways.clear(); | ||
ways.resize(size + 1, 0); | ||
// There's 1 way to create any possible first prefix. | ||
ways[0] = 1; | ||
|
||
for start in 0..size { | ||
// Only consider suffixes that have a valid prefix. | ||
if ways[start] > 0 { | ||
// Walk trie from root to leaf. | ||
let mut i = 0; | ||
|
||
for end in start..size { | ||
// Get next link. | ||
let j = to_index(design[end]); | ||
i = trie[i].next[j]; | ||
|
||
// This is not a valid prefix, stop the search. | ||
if i == 0 { | ||
break; | ||
} | ||
|
||
// Add the number of possible ways this prefix can be reached. | ||
if trie[i].towel { | ||
ways[end + 1] += ways[start]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Last element is the total possible combinations. | ||
let total = ways[size]; | ||
part_one += (total > 0) as u64; | ||
part_two += total; | ||
} | ||
|
||
(part_one, part_two) | ||
} | ||
|
||
pub fn part1(input: &Input) -> u64 { | ||
input.0 | ||
} | ||
|
||
pub fn part2(input: &Input) -> u64 { | ||
input.1 | ||
} | ||
|
||
/// Convenience function to map `a..z` to `0..26`. | ||
#[inline] | ||
fn to_index(b: u8) -> usize { | ||
(b - b'a') as usize | ||
} | ||
|
||
/// Simple Node object that uses indices to link to other nodes. | ||
/// There are only 26 letters so store links in a fixed size array. | ||
struct Node { | ||
towel: bool, | ||
next: [usize; 26], | ||
} | ||
|
||
impl Node { | ||
fn new() -> Self { | ||
Node { towel: false, next: [0; 26] } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
use aoc::year2024::day19::*; | ||
|
||
const EXAMPLE: &str = "\ | ||
r, wr, b, g, bwu, rb, gb, br | ||
brwrr | ||
bggr | ||
gbbr | ||
rrbgbr | ||
ubwu | ||
bwurrg | ||
brgr | ||
bbrgwb"; | ||
|
||
#[test] | ||
fn part1_test() { | ||
let input = parse(EXAMPLE); | ||
assert_eq!(part1(&input), 6); | ||
} | ||
|
||
#[test] | ||
fn part2_test() { | ||
let input = parse(EXAMPLE); | ||
assert_eq!(part2(&input), 16); | ||
} |