generated from janezicmatej/aoc-template
	solution: day 12
This commit is contained in:
		
							
								
								
									
										6
									
								
								data/examples/12.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								data/examples/12.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
???.### 1,1,3
 | 
			
		||||
.??..??...?##. 1,1,3
 | 
			
		||||
?#?#?#?#?#?#?#? 1,3,1,6
 | 
			
		||||
????.#...#... 4,1,1
 | 
			
		||||
????.######..#####. 1,6,5
 | 
			
		||||
?###???????? 3,2,1
 | 
			
		||||
							
								
								
									
										114
									
								
								src/bin/12.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/bin/12.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
use std::{collections::HashMap, iter::once};
 | 
			
		||||
 | 
			
		||||
fn is_valid(sequence: &[char], ptr: usize, group: usize) -> bool {
 | 
			
		||||
    let edges_front = *once(&'.').chain(sequence.iter()).nth(ptr).unwrap() != '#';
 | 
			
		||||
    let edges_back = *sequence.iter().chain(once(&'.')).nth(ptr + group).unwrap() != '#';
 | 
			
		||||
 | 
			
		||||
    let filled = sequence
 | 
			
		||||
        .iter()
 | 
			
		||||
        .chain(once(&'.').cycle())
 | 
			
		||||
        .skip(ptr)
 | 
			
		||||
        .take(group)
 | 
			
		||||
        .all(|x| *x != '.');
 | 
			
		||||
 | 
			
		||||
    edges_front && edges_back && filled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn count(
 | 
			
		||||
    memo: &mut HashMap<(usize, usize), usize>,
 | 
			
		||||
    sequence: &[char],
 | 
			
		||||
    groups: &[usize],
 | 
			
		||||
    ptr: usize,
 | 
			
		||||
) -> usize {
 | 
			
		||||
    match groups.split_first() {
 | 
			
		||||
        None => !sequence.iter().skip(ptr).any(|c| *c == '#') as usize,
 | 
			
		||||
        Some((group, r_groups)) => {
 | 
			
		||||
            let remaining = r_groups.iter().sum();
 | 
			
		||||
            let mut total = 0;
 | 
			
		||||
 | 
			
		||||
            for idx in ptr..(sequence.len() - group - remaining + 1) {
 | 
			
		||||
                if is_valid(sequence, idx, *group) {
 | 
			
		||||
                    let next = idx + *group + 1;
 | 
			
		||||
                    match memo.get(&(remaining, next)) {
 | 
			
		||||
                        Some(m) => total += m,
 | 
			
		||||
                        None => {
 | 
			
		||||
                            let count = count(memo, sequence, r_groups, next);
 | 
			
		||||
                            memo.insert((remaining, next), count);
 | 
			
		||||
                            total += count
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if sequence[idx] == '#' {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            total
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_line(line: &str) -> (Vec<char>, Vec<usize>) {
 | 
			
		||||
    let (str_seq, str_grp) = line.split_once(' ').unwrap();
 | 
			
		||||
    let sequence = str_seq.chars().collect();
 | 
			
		||||
    let groups = str_grp.split(',').filter_map(|x| x.parse().ok()).collect();
 | 
			
		||||
 | 
			
		||||
    (sequence, groups)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn unfold(sequence: Vec<char>, groups: Vec<usize>, n: usize) -> (Vec<char>, Vec<usize>) {
 | 
			
		||||
    let seq_len = sequence.len();
 | 
			
		||||
    let grp_len = groups.len();
 | 
			
		||||
 | 
			
		||||
    let new_sequence = sequence
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .chain(once('?'))
 | 
			
		||||
        .cycle()
 | 
			
		||||
        .take(seq_len * n + n - 1)
 | 
			
		||||
        .collect();
 | 
			
		||||
    let new_groups = groups.into_iter().cycle().take(grp_len * n).collect();
 | 
			
		||||
 | 
			
		||||
    (new_sequence, new_groups)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn part_one(input: &str) -> Option<usize> {
 | 
			
		||||
    Some(
 | 
			
		||||
        input
 | 
			
		||||
            .lines()
 | 
			
		||||
            .map(parse_line)
 | 
			
		||||
            .map(|(sequence, groups)| count(&mut HashMap::new(), &sequence, &groups, 0))
 | 
			
		||||
            .sum(),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn part_two(input: &str) -> Option<usize> {
 | 
			
		||||
    Some(
 | 
			
		||||
        input
 | 
			
		||||
            .lines()
 | 
			
		||||
            .map(parse_line)
 | 
			
		||||
            .map(|(sequence, groups)| unfold(sequence, groups, 5))
 | 
			
		||||
            .map(|(sequence, groups)| count(&mut HashMap::new(), &sequence, &groups, 0))
 | 
			
		||||
            .sum(),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
aoc::solution!(12);
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_part_one() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            part_one(&aoc::template::read_file("examples", 12)),
 | 
			
		||||
            Some(21)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_part_two() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            part_two(&aoc::template::read_file("examples", 12)),
 | 
			
		||||
            Some(525152)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user