2023-12-12 13:04:24 +01:00
#![ feature(test) ]
extern crate test ;
use aoc2023 ::{ boilerplate , common ::* } ;
2023-12-12 16:21:36 +01:00
use fnv ::FnvHashMap ;
2023-12-12 13:04:24 +01:00
use itertools ::Itertools ;
2023-12-12 16:21:36 +01:00
use std ::mem ::transmute ;
2023-12-12 13:04:24 +01:00
const DAY : usize = 12 ;
2023-12-12 16:21:36 +01:00
type I = usize ;
2023-12-12 13:04:24 +01:00
type Parsed < ' a > = Vec < ( & ' a [ Spring ] , Vec < I > ) > ;
#[ repr(u8) ]
#[ derive(Debug, PartialEq, Eq, Copy, Clone) ]
#[ allow(dead_code) ]
enum Spring {
Operational = b '.' ,
Broken = b '#' ,
Unknown = b '?' ,
}
fn parse_input ( raw : & str ) -> Parsed {
raw . lines ( )
. map ( | l | l . split_once ( ' ' ) . unwrap ( ) )
. map ( | ( springs , counts ) | ( unsafe { transmute ( springs ) } , parse_nums_separator ( counts , ',' ) ) )
. collect ( )
}
2023-12-12 16:21:36 +01:00
fn part1 ( lines : & Parsed ) -> usize {
lines
2023-12-12 13:04:24 +01:00
. iter ( )
2023-12-12 16:21:36 +01:00
. map ( | ( springs , expected ) | {
let mut cache = FnvHashMap ::default ( ) ;
valid_combinations_recursive ( & springs , & expected , 0 , 0 , 0 , 0 , false , & mut cache )
} )
. sum ( )
2023-12-12 13:04:24 +01:00
}
2023-12-12 16:21:36 +01:00
fn valid_combinations_recursive (
springs : & [ Spring ] ,
constraints : & [ usize ] ,
index : usize ,
broken_count : usize ,
current_constraint : usize ,
broken_streak : usize ,
just_completed_streak : bool ,
cache : & mut FnvHashMap < ( usize , usize , usize , usize , bool ) , usize > ,
) -> usize {
if let Some ( & cached ) = cache . get ( & ( index , broken_count , current_constraint , broken_streak , just_completed_streak ) ) {
return cached ;
}
if index = = springs . len ( ) {
let valid = broken_count = = constraints . iter ( ) . sum ::< usize > ( )
& & current_constraint = = constraints . len ( )
& & ( broken_streak = = 0 | | just_completed_streak ) ;
return valid as usize ;
2023-12-12 13:04:24 +01:00
}
2023-12-12 16:21:36 +01:00
let valid = match springs [ index ] {
Spring ::Operational = > {
valid_combinations_recursive ( springs , constraints , index + 1 , broken_count , current_constraint , 0 , false , cache )
}
Spring ::Broken = > {
if current_constraint < constraints . len ( )
& & broken_count < constraints . iter ( ) . take ( current_constraint + 1 ) . sum ( )
& & ! just_completed_streak
{
let just_completed_streak = broken_streak + 1 = = constraints [ current_constraint ] ;
valid_combinations_recursive (
springs ,
constraints ,
index + 1 ,
broken_count + 1 ,
current_constraint + just_completed_streak as usize ,
broken_streak + 1 ,
just_completed_streak ,
cache ,
)
} else {
0
}
}
Spring ::Unknown = > {
let operational =
valid_combinations_recursive ( springs , constraints , index + 1 , broken_count , current_constraint , 0 , false , cache ) ;
let broken = if current_constraint < constraints . len ( )
& & broken_count < constraints . iter ( ) . take ( current_constraint + 1 ) . sum ( )
& & ! just_completed_streak
{
let just_completed_streak = broken_streak + 1 = = constraints [ current_constraint ] ;
valid_combinations_recursive (
springs ,
constraints ,
index + 1 ,
broken_count + 1 ,
current_constraint + just_completed_streak as usize ,
broken_streak + 1 ,
just_completed_streak ,
cache ,
)
} else {
0
} ;
operational + broken
}
} ;
cache . insert ( ( index , broken_count , current_constraint , broken_streak , just_completed_streak ) , valid ) ;
valid
2023-12-12 13:04:24 +01:00
}
2023-12-12 16:21:36 +01:00
fn part2 ( lines : & Parsed ) -> usize {
2023-12-12 13:04:24 +01:00
lines
. iter ( )
. map ( | ( springs , expected ) | {
2023-12-12 16:21:36 +01:00
let springs = springs . to_vec ( ) ;
let new_spring_length = springs . len ( ) * 5 + 4 ;
(
springs . into_iter ( ) . chain ( std ::iter ::once ( Spring ::Unknown ) ) . cycle ( ) . take ( new_spring_length ) . collect_vec ( ) ,
expected . iter ( ) . cloned ( ) . cycle ( ) . take ( expected . len ( ) * 5 ) . collect_vec ( ) ,
)
} )
. map ( | ( springs , expected ) | {
let mut cache = FnvHashMap ::default ( ) ;
valid_combinations_recursive ( & springs , & expected , 0 , 0 , 0 , 0 , false , & mut cache )
2023-12-12 13:04:24 +01:00
} )
. sum ( )
}
boilerplate! {
TEST_INPUT = = " \
? ? ? . ### 1 , 1 , 3
. ? ? .. ? ? .. . ? ##. 1 , 1 , 3
? #? #? #? #? #? #? #? 1 , 3 , 1 , 6
? ? ? ? . #.. . #.. . 4 , 1 , 1
? ? ? ? . ######.. #####. 1 , 6 , 5
? ###? ? ? ? ? ? ? ? 3 , 2 , 1 " ,
tests : {
part1 : { TEST_INPUT = > 21 } ,
2023-12-12 16:21:36 +01:00
part2 : { TEST_INPUT = > 525152 } ,
2023-12-12 13:04:24 +01:00
} ,
bench1 = = 7260 ,
2023-12-12 16:21:36 +01:00
bench2 = = 1909291258644 ,
2023-12-12 13:04:24 +01:00
bench_parse : Vec ::len = > 1000 ,
}