Skip to content

Commit

Permalink
Year 2024 Day 19
Browse files Browse the repository at this point in the history
  • Loading branch information
maneatingape committed Dec 19, 2024
1 parent 9f684be commit 7f442ce
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
| 16 | [Reindeer Maze](https://adventofcode.com/2024/day/16) | [Source](src/year2024/day16.rs) | 390 |
| 17 | [Chronospatial Computer](https://adventofcode.com/2024/day/17) | [Source](src/year2024/day17.rs) | 2 |
| 18 | [RAM Run](https://adventofcode.com/2024/day/18) | [Source](src/year2024/day18.rs) | 42 |
| 19 | [Linen Layout](https://adventofcode.com/2024/day/19) | [Source](src/year2024/day19.rs) | 138 |

## 2023

Expand Down
2 changes: 1 addition & 1 deletion benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,5 @@ benchmark!(year2023

benchmark!(year2024
day01, day02, day03, day04, day05, day06, day07, day08, day09, day10, day11, day12, day13,
day14, day15, day16, day17, day18
day14, day15, day16, day17, day18, day19
);
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,5 @@ library!(year2023 "Restore global snow production."

library!(year2024 "Locate the Chief Historian in time for the big Christmas sleigh launch."
day01, day02, day03, day04, day05, day06, day07, day08, day09, day10, day11, day12, day13,
day14, day15, day16, day17, day18
day14, day15, day16, day17, day18, day19
);
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,5 @@ run!(year2023

run!(year2024
day01, day02, day03, day04, day05, day06, day07, day08, day09, day10, day11, day12, day13,
day14, day15, day16, day17, day18
day14, day15, day16, day17, day18, day19
);
111 changes: 111 additions & 0 deletions src/year2024/day19.rs
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] }
}
}
2 changes: 1 addition & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,5 @@ test!(year2023

test!(year2024
day01, day02, day03, day04, day05, day06, day07, day08, day09, day10, day11, day12, day13,
day14, day15, day16, day17, day18
day14, day15, day16, day17, day18, day19
);
25 changes: 25 additions & 0 deletions tests/year2024/day19.rs
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);
}

0 comments on commit 7f442ce

Please sign in to comment.