diff --git a/src/main.rs b/src/main.rs index c2070bf..88b1e61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, io::stdin, ops::Range}; +use std::{collections::HashMap, io::stdin}; use anyhow::Result; @@ -15,95 +15,19 @@ fn matches(conditions: &str, group: &str) -> bool { true } -// fn arrangements3_inner( -// conditions: &str, -// start: usize, -// groups: &[usize], -// cache: &mut Cache, -// ranges: &mut Vec>, -// ) -> i64 { -// // println!("arrangements3_inner({conditions:?}, {groups:?}, {ranges:?}"); -// if groups.is_empty() { -// let mut s = String::new(); -// for i in 0..conditions.len() { -// if ranges.iter().any(|r| r.contains(&i)) { -// s.push('#'); -// } else { -// s.push('.'); -// } -// } -// let mut m = true; -// for (c, s) in conditions.chars().zip(s.chars()) { -// if c == '?' { -// continue; -// } -// if c == s { -// continue; -// } -// m = false; -// } -// if m { -// return 1; -// } -// return 0; -// } -// // let mut start = ranges.last().unwrap_or(&(0..0usize)).end; -// // if start > 0 { -// // start += 1; -// // } -// let w = conditions.len() - start - groups.iter().skip(1).sum::(); -// let g = groups[0]; -// // println!("start={start} w={w} g={g}"); -// if w < g { -// return 0; -// } -// let positions = w - g + 1; -// let mut matches = 0; -// for i in start..(start + positions) { -// ranges.push(i..(i + g)); -// let new_start = i + g + 1; -// let hash_key = format!("{},{:?},{:?}", new_start, &groups[1..], ranges); -// if let Some(value) = cache.get(&hash_key) { -// println!("cache hit"); -// matches += value; -// } else { -// // println!("cache miss"); -// let a = arrangements3_inner(conditions, new_start, &groups[1..], cache, ranges); -// cache.insert(hash_key, a); -// // insert into cache here -// matches += a; -// } -// ranges.pop(); -// } -// matches -// } -// -// type Cache = HashMap; -// -// fn arrangements3(conditions: &str, groups: &[usize]) -> i64 { -// let mut ranges = vec![]; -// let mut cache: HashMap = HashMap::new(); -// arrangements3_inner(conditions, 0, groups, &mut cache, &mut ranges) -// } +type Cache = HashMap; -fn arrangements2_inner(conditions: &str, groups: &[String]) -> i64 { +fn arrangements_inner(conditions: &str, groups: &[String], cache: &mut Cache) -> i64 { let g = &groups[0]; let w = conditions.len() - groups.iter().skip(1).map(|s| s.len()).sum::(); - // println!( - // "{}conditions={conditions} groups={groups:?} w={w} g={g}", - // " ".repeat(depth) - // ); let positions = w - g.len() + 1; - // println!("positions={positions}"); let mut sum = 0; for i in 0..positions { if !matches(&conditions[i..], g) { - // println!("{}i={i} no match", " ".repeat(depth)); continue; } if conditions[..i].contains('#') { - // println!("{}i={i} found a # somewhere before", " ".repeat(depth)); continue; } let after = if (i + g.len()) < conditions.len() { @@ -112,156 +36,40 @@ fn arrangements2_inner(conditions: &str, groups: &[String]) -> i64 { '.' }; if after == '#' { - // println!("{}i={i} found a # right after", " ".repeat(depth)); continue; } if groups.len() == 1 { if conditions[i + g.len()..].contains('#') { - // println!( - // "{}i={i} last group, but there are unmatched '#'s after", - // " ".repeat(depth) - // ); continue; } - // println!("{}i={i} last group, counting", " ".repeat(depth)); sum += 1; } else { - // println!("{}i={i}:", " ".repeat(depth)); - sum += arrangements2_inner(&conditions[i + g.len()..], &groups[1..]); + let cache_key = format!("{},{:?}", &conditions[i + g.len()..], &groups[1..]); + if let Some(v) = cache.get(&cache_key) { + sum += v; + } else { + let a = arrangements_inner(&conditions[i + g.len()..], &groups[1..], cache); + cache.insert(cache_key, a); + sum += a; + } } } sum } -fn arrangements2(conditions: &str, groups: &[usize]) -> i64 { +fn arrangements(conditions: &str, groups: &[usize]) -> i64 { let mut group_strings = vec![]; group_strings.push("#".repeat(groups[0]).to_owned()); for g in groups.iter().skip(1) { group_strings.push(".".to_owned() + &"#".to_owned().repeat(*g)); } - arrangements2_inner(conditions, &group_strings) + let mut cache: Cache = Cache::new(); + arrangements_inner(conditions, &group_strings, &mut cache) } -// fn arrangements_inner(conditions: &str, groups: &[usize], indent: usize) -> i64 { -// println!( -// "{}arrangements(conditions: {}, groups: {:?})", -// " ".repeat(indent), -// conditions, -// groups -// ); -// if groups.is_empty() { -// return 0; -// } -// -// let g = groups[0]; -// -// // the max width for the 1st group is the length of the conditions string -// // minus the minimum width of the rest of the groups -// // which would be the length of each group (i.e. each # char) plus 1 (i.e. the .) -// let max_width: usize = conditions.len() - (groups.iter().skip(1).map(|n| n + 1).sum::()); -// println!(" {}g={g}", " ".repeat(indent)); -// println!(" {}max_width={max_width}", " ".repeat(indent)); -// -// // if max_width == 1 { -// // return 1; -// // } -// -// // the possible positions for the group would then be the width available -// // for it, minus its own width, plus 1 -// // -// // ????. -// // ##... -// // .##.. -// // ..##. -// // ...## -// let positions = max_width - g + 1; -// -// println!(" {}positions={positions}", " ".repeat(indent)); -// let mut sum = 0; -// for i in 0..positions { -// let mut matches = true; -// for (j, c) in conditions.chars().take(max_width).enumerate() { -// if (i..(i + g)).contains(&j) { -// // inside the group, the conditions need to be # or ? -// if c == '.' { -// println!( -// " {}i={i} j={j} found a dot inside the group", -// " ".repeat(indent) -// ); -// matches = false; -// break; -// } -// } else { -// // outside the group, the conditions need to be . or ? -// if c == '#' { -// println!( -// " {}i={i} j={j} found a # outside the group", -// " ".repeat(indent) -// ); -// matches = false; -// break; -// } -// } -// } -// if !matches { -// println!(" {}i={i} no match", " ".repeat(indent)); -// continue; -// } -// // if (conditions[i..(i + g)]).contains('.') { -// // println!( -// // " {}i={i} conditions[{i}..{}]({}) contains a dot", -// // " ".repeat(indent), -// // i + g, -// // &conditions[i..(i + g)] -// // ); -// // continue; -// // } -// // if i + g >= conditions.len() { -// // println!(" {}i={i} # nothing left, counts", " ".repeat(indent)); -// // // nothing left on the right -// // sum += 1; // count this arrangement -// // continue; -// // } -// // // after the group, there has to be a '.' or a '?' -// // if conditions.chars().nth(i + g) == Some('#') { -// // println!(" {}i={i} # found after", " ".repeat(indent)); -// // continue; -// // } -// // // before the group, there has to be a '.' or a '?' or the beginning of the string -// // if i > 0 && conditions.chars().nth(i - 1) == Some('#') { -// // println!(" {}i={i} # found before", " ".repeat(indent)); -// // continue; -// // } -// // // // after the group, there needs to be a dot, or the end of the string -// // // let valid = match conditions.get(i + g..i + g + 1) { -// // // Some(c) => c == ".", -// // // None => true, -// // // }; -// // // if !valid { -// // // continue; -// // // } -// if groups.len() == 1 { -// println!(" {}i={i} counts", " ".repeat(indent)); -// sum += 1; -// } else { -// let a = arrangements_inner(&conditions[(i + g + 1)..], &groups[1..], indent + 1); -// sum += a; -// println!(" {}returned {a}", " ".repeat(indent),); -// } -// } -// sum -// } -// -// fn arrangements(conditions: &str, groups: &[usize]) -> i64 { -// arrangements_inner(conditions, groups, 0) -// } - fn main() -> Result<()> { - // println!("{}", arrangements("???.###", &[1, 1, 3], 0)); - // println!("{}", arrangements3(".??.?#??##???.", &[1, 6])); let mut sum = 0; for line in stdin().lines().map_while(Result::ok) { - // print!("{line} "); let mut split = line.split_whitespace(); let conditions = split.next().unwrap(); let groups: Vec = split @@ -272,12 +80,7 @@ fn main() -> Result<()> { .map_while(Result::ok) .collect(); - // let conditions = conditions.repeat(5); - // let groups = groups.repeat(5); - - // println!("{conditions} {groups:?}"); - let a = arrangements2(&conditions, &groups); - // println!("{a}"); + let a = arrangements(conditions, &groups); sum += a; } println!("{sum}"); @@ -288,40 +91,18 @@ fn main() -> Result<()> { mod tests { use super::*; - // #[test] - // fn test_arrangements() { - // assert_eq!(arrangements(".??.?#??##???.", &[1, 6]), 5); - // assert_eq!(arrangements("???.###", &[1, 1, 3]), 1); - // assert_eq!(arrangements(".??..??...?##.", &[1, 1, 3]), 4); - // assert_eq!(arrangements("?#?#?#?#?#?#?#?", &[1, 3, 1, 6]), 1); - // assert_eq!(arrangements("????.#...#...", &[4, 1, 1]), 1); - // assert_eq!(arrangements("????.######..#####.", &[1, 6, 5]), 4); - // assert_eq!(arrangements("?###????????", &[3, 2, 1]), 10); - // } - #[test] - fn test_arrangements2() { - // assert_eq!(arrangements2(".??.?#??##???.", &[1, 6]), 5); - // assert_eq!(arrangements2("???.###", &[1, 1, 3]), 1); - // assert_eq!(arrangements2(".??..??...?##.", &[1, 1, 3]), 4); - // assert_eq!(arrangements2("?#?#?#?#?#?#?#?", &[1, 3, 1, 6]), 1); - // assert_eq!(arrangements2("????.#...#...", &[4, 1, 1]), 1); - // assert_eq!(arrangements2("????.######..#####.", &[1, 6, 5]), 4); - // assert_eq!(arrangements2("?###????????", &[3, 2, 1]), 10); - assert_eq!(arrangements2("????#?#?..?#?", &[4, 1]), 2); + fn test_arrangements() { + assert_eq!(arrangements(".??.?#??##???.", &[1, 6]), 5); + assert_eq!(arrangements("???.###", &[1, 1, 3]), 1); + assert_eq!(arrangements(".??..??...?##.", &[1, 1, 3]), 4); + assert_eq!(arrangements("?#?#?#?#?#?#?#?", &[1, 3, 1, 6]), 1); + assert_eq!(arrangements("????.#...#...", &[4, 1, 1]), 1); + assert_eq!(arrangements("????.######..#####.", &[1, 6, 5]), 4); + assert_eq!(arrangements("?###????????", &[3, 2, 1]), 10); + assert_eq!(arrangements("????#?#?..?#?", &[4, 1]), 2); } - // #[test] - // fn test_arrangements3() { - // assert_eq!(arrangements3(".??.?#??##???.", &[1, 6]), 5); - // assert_eq!(arrangements2("???.###", &[1, 1, 3]), 1); - // assert_eq!(arrangements2(".??..??...?##.", &[1, 1, 3]), 4); - // assert_eq!(arrangements2("?#?#?#?#?#?#?#?", &[1, 3, 1, 6]), 1); - // assert_eq!(arrangements2("????.#...#...", &[4, 1, 1]), 1); - // assert_eq!(arrangements2("????.######..#####.", &[1, 6, 5]), 4); - // assert_eq!(arrangements2("?###????????", &[3, 2, 1]), 10); - // } - #[test] fn test_matches() { assert!(matches("?", "#"));