Skip to content

Commit

Permalink
day 12
Browse files Browse the repository at this point in the history
  • Loading branch information
kcaffrey committed Dec 12, 2023
1 parent d4d8323 commit 3f25073
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 12 deletions.
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `34.3µs` | `35.6µs` |
| [Day 2](./src/bin/02.rs) | `42.3µs` | `41.9µs` |
| [Day 3](./src/bin/03.rs) | `84.6µs` | `99.4µs` |
| [Day 1](./src/bin/01.rs) | `35.3µs` | `36.5µs` |
| [Day 2](./src/bin/02.rs) | `42.2µs` | `41.8µs` |
| [Day 3](./src/bin/03.rs) | `84.0µs` | `99.0µs` |
| [Day 4](./src/bin/04.rs) | `49.0µs` | `49.5µs` |
| [Day 5](./src/bin/05.rs) | `20.4µs` | `24.2µs` |
| [Day 6](./src/bin/06.rs) | `221.0ns` | `104.0ns` |
| [Day 7](./src/bin/07.rs) | `103.4µs` | `96.2µs` |
| [Day 8](./src/bin/08.rs) | `72.2µs` | `160.0µs` |
| [Day 9](./src/bin/09.rs) | `53.1µs` | `47.1µs` |
| [Day 10](./src/bin/10.rs) | `262.9µs` | `279.8µs` |
| [Day 11](./src/bin/11.rs) | `16.2µs` | `15.8µs` |

**Total: 1.59ms**
| [Day 5](./src/bin/05.rs) | `20.6µs` | `24.5µs` |
| [Day 6](./src/bin/06.rs) | `203.0ns` | `101.0ns` |
| [Day 7](./src/bin/07.rs) | `93.3µs` | `91.4µs` |
| [Day 8](./src/bin/08.rs) | `74.6µs` | `161.1µs` |
| [Day 9](./src/bin/09.rs) | `58.7µs` | `55.4µs` |
| [Day 10](./src/bin/10.rs) | `264.9µs` | `275.0µs` |
| [Day 11](./src/bin/11.rs) | `16.5µs` | `15.8µs` |
| [Day 12](./src/bin/12.rs) | `129.4µs` | `632.8µs` |

**Total: 2.35ms**
<!--- benchmarking table --->

---
Expand Down
6 changes: 6 additions & 0 deletions data/examples/12.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
163 changes: 163 additions & 0 deletions src/bin/12.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use rayon::{iter::ParallelIterator, str::ParallelString};
use tinyvec::ArrayVec;

advent_of_code::solution!(12);

pub fn part_one(input: &str) -> Option<u64> {
Some(input.par_lines().map(|line| solve_line(line, 1)).sum())
}

pub fn part_two(input: &str) -> Option<u64> {
Some(input.par_lines().map(|line| solve_line(line, 5)).sum())
}

fn solve_line(line: &str, copies: usize) -> u64 {
let (spring_records, damaged_counts) = line.split_once(' ').expect("should be valid input");
let spring_records = vec![spring_records; copies].join("?");
let spring_records = spring_records.as_bytes();

let damaged_counts = std::iter::repeat(
damaged_counts
.split(',')
.map(|s| s.parse::<u32>().expect("should be valid spring count")),
)
.take(copies)
.flatten()
.collect::<ArrayVec<[u32; 64]>>();

// solve_brute_force_recursion(spring_records, &damaged_counts, 0)
solve_dp(spring_records, &damaged_counts)
}

fn solve_dp(spring_records: &[u8], damaged_counts: &[u32]) -> u64 {
// Precompute how many springs could be damaged in a row ending at each spring.
let mut damaged_runs = vec![0; spring_records.len()];
let mut cur_damaged_run = 0;
let mut first_damaged = spring_records.len();
for (i, record) in spring_records.iter().copied().enumerate() {
if record != b'.' {
cur_damaged_run += 1;
} else {
cur_damaged_run = 0;
}
if record == b'#' && i < first_damaged {
first_damaged = i;
}
damaged_runs[i] = cur_damaged_run;
}

// Solve the problem using DP, for f(M, N) where M is the number of
// damaged springs that have been "resolved" and N is how many springs are used from the input.
let mut prev = vec![0; spring_records.len() + 1];
let mut cur = vec![0; spring_records.len() + 1];
(0..=first_damaged).for_each(|i| {
prev[i] = 1;
});
for (damaged_index, damaged_count) in damaged_counts.iter().copied().enumerate() {
let damaged_count = damaged_count as usize;
cur[0] = 0;
for end_of_spring in 0..spring_records.len() {
let last_record = spring_records[end_of_spring];
let mut count = 0;
if last_record == b'.' || last_record == b'?' {
// This record can be operational. The count is the same
// as f(damaged_count, end_of_spring).
count += end_of_spring
.checked_sub(0)
.map(|index| cur[index])
.unwrap_or(0);
}
if last_record == b'#' || last_record == b'?' {
// This record can be counted as damaged.
// First we see if this can even be considered the end of the next run
// of damaged springs. To be considered the end of such a run, the next
// spring cannot be damaged, and the previous springs (corresponding to the
// next damaged count) have to be either unknown or damaged.
// Finally, the character preceding the run of damaged springs must not
// be damaged (so either . or ?).
let next_is_definite_damaged = spring_records
.get(end_of_spring + 1)
.filter(|&&v| v == b'#')
.is_some();
let has_correct_damaged_count =
!next_is_definite_damaged && damaged_runs[end_of_spring] >= damaged_count;
let has_correct_preceding = has_correct_damaged_count
&& end_of_spring
.checked_sub(damaged_count)
.map(|i| spring_records[i])
.filter(|&v| v == b'#')
.is_none();
if has_correct_preceding {
count += end_of_spring
.checked_sub(damaged_count)
.map(|index| prev[index])
.unwrap_or(if damaged_index == 0 { 1 } else { 0 });
}
}

cur[end_of_spring + 1] = count;
}
std::mem::swap(&mut prev, &mut cur);
}
prev[spring_records.len()]
}

#[allow(unused)]
fn solve_brute_force_recursion(
spring_records: &[u8],
damaged_counts: &[u32],
current_damaged_length: u32,
) -> u64 {
if spring_records.is_empty() && damaged_counts.is_empty() && current_damaged_length == 0
|| spring_records.is_empty()
&& damaged_counts.len() == 1
&& current_damaged_length == damaged_counts[0]
{
return 1;
} else if spring_records.is_empty()
|| damaged_counts.is_empty() && current_damaged_length > 0
|| !damaged_counts.is_empty() && current_damaged_length > damaged_counts[0]
{
return 0;
}

let mut count = 0;
if spring_records[0] == b'.' || spring_records[0] == b'?' {
// Next record can be operational.
count += if current_damaged_length > 0 {
if current_damaged_length == damaged_counts[0] {
solve_brute_force_recursion(&spring_records[1..], &damaged_counts[1..], 0)
} else {
0
}
} else {
solve_brute_force_recursion(&spring_records[1..], damaged_counts, 0)
};
}
if spring_records[0] == b'#' || spring_records[0] == b'?' {
// Next record can be damaged.
count += solve_brute_force_recursion(
&spring_records[1..],
damaged_counts,
current_damaged_length + 1,
);
}
count
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(21));
}

#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(525152));
}
}

0 comments on commit 3f25073

Please sign in to comment.