diff --git a/src/lib.rs b/src/lib.rs index 85b45724..f571f0bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1484,6 +1484,7 @@ pub mod problem_2011_final_value_of_variable_after_performing_operations; pub mod problem_2012_sum_of_beauty_in_the_array; pub mod problem_2016_maximum_difference_between_increasing_elements; pub mod problem_2017_grid_game; +pub mod problem_2018_check_if_word_can_be_placed_in_crossword; pub mod problem_2022_convert_1d_array_into_2d_array; pub mod problem_2023_number_of_pairs_of_strings_with_concatenation_equal_to_target; pub mod problem_2024_maximize_the_confusion_of_an_exam; diff --git a/src/problem_2018_check_if_word_can_be_placed_in_crossword/mod.rs b/src/problem_2018_check_if_word_can_be_placed_in_crossword/mod.rs new file mode 100644 index 00000000..c9d78a50 --- /dev/null +++ b/src/problem_2018_check_if_word_can_be_placed_in_crossword/mod.rs @@ -0,0 +1,44 @@ +pub mod recursive; + +pub trait Solution { + fn place_word_in_crossword(board: Vec>, word: String) -> bool; +} + +#[cfg(test)] +mod tests { + use super::Solution; + + pub fn run() { + let test_cases = [ + ((&["# #", " #", "#c "] as &[_], "abc"), true), + ((&[" #a", " #c", " #a"], "ac"), false), + ((&["# #", " #", "# c"], "ca"), true), + ((&[" #o tmo # "], "octmor"), true), + ((&["# #", "# #", "# c"], "ca"), true), + ((&[" ", " "], "a"), false), + ((&[" cz", "###"], "cz"), false), + ( + ( + &[ + " ", " ", " ", " ", "#", "#", " ", "b", "#", " ", " ", " ", " ", " ", " ", " ", " ", "c", " ", + "#", + ], + "ba", + ), + true, + ), + ((&["c #", "###"], "ab"), false), + ((&[" ", "#", "o", " ", "t", "m", "o", " ", "#", " "], "octmor"), true), + ]; + + for ((board, word), expected) in test_cases { + assert_eq!( + S::place_word_in_crossword( + board.iter().map(|row| row.chars().collect()).collect(), + word.to_string(), + ), + expected, + ); + } + } +} diff --git a/src/problem_2018_check_if_word_can_be_placed_in_crossword/recursive.rs b/src/problem_2018_check_if_word_can_be_placed_in_crossword/recursive.rs new file mode 100644 index 00000000..591e4e63 --- /dev/null +++ b/src/problem_2018_check_if_word_can_be_placed_in_crossword/recursive.rs @@ -0,0 +1,95 @@ +pub struct Solution; + +// ------------------------------------------------------ snip ------------------------------------------------------ // + +impl Solution { + fn check(mut line_iter: impl Iterator, word: &[u8], tag: u8) -> bool { + 'outer: loop { + let mut word_iter = word.iter().copied(); + + loop { + let c = word_iter.next(); + + if let Some(pattern) = line_iter.next() { + match pattern { + b' ' => { + if c.is_some() { + continue; + } + } + b'#' => { + if c.is_none() { + return true; + } + + break; + } + _ => { + if c == Some(pattern) { + continue; + } + } + } + } else { + return c.is_none(); + } + + loop { + match line_iter.next() { + None => return false, + Some(b'#') => continue 'outer, + _ => {} + } + } + } + } + } + + pub fn place_word_in_crossword(board: Vec>, word: String) -> bool { + let rows = board.len(); + let columns = board.first().map_or(0, Vec::len); + let mut flat_board = Vec::with_capacity(columns * rows); + + for row in board { + flat_board.extend(row.into_iter().map(|c| c as u8)); + } + + let word = word.as_bytes(); + + for row in flat_board.chunks_exact(columns) { + if Self::check(row.iter().copied(), word, 0) || Self::check(row.iter().copied().rev(), word, 1) { + return true; + } + } + + let mut iter = flat_board.iter().copied(); + + for _ in 0..columns { + let column_iter = iter.clone().step_by(columns); + + if Self::check(column_iter.clone(), word, 2) || Self::check(column_iter.rev(), word, 3) { + return true; + } + + iter.next(); + } + + false + } +} + +// ------------------------------------------------------ snip ------------------------------------------------------ // + +impl super::Solution for Solution { + fn place_word_in_crossword(board: Vec>, word: String) -> bool { + Self::place_word_in_crossword(board, word) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_solution() { + super::super::tests::run::(); + } +}