diff --git a/src/problem/mod.rs b/src/problem/mod.rs index 7d1d3ec..d9dc71e 100644 --- a/src/problem/mod.rs +++ b/src/problem/mod.rs @@ -160,4 +160,5 @@ mod p399_evaluate_division; mod p207_course_schedule; mod p210_course_schedule_ii; mod p909_snakes_and_ladders; -mod p433_minimum_genetic_mutation; \ No newline at end of file +mod p433_minimum_genetic_mutation; +mod p127_word_ladder; \ No newline at end of file diff --git a/src/problem/p127_word_ladder.rs b/src/problem/p127_word_ladder.rs new file mode 100644 index 0000000..db2de9e --- /dev/null +++ b/src/problem/p127_word_ladder.rs @@ -0,0 +1,175 @@ +/** + * [127] Word Ladder + */ +pub struct Solution {} + +// submission codes start here +use std::collections::HashSet; + +impl Solution { + pub fn ladder_length(begin_word: String, end_word: String, word_list: Vec) -> i32 { + let mut start = usize::MAX; + let mut start_found = false; + let mut end_found = false; + let mut end = usize::MAX; + + for (i, word) in word_list.iter().enumerate() { + if *word == begin_word { + start_found = true; + start = i; + break; + } + } + + let mut neighbors = vec![HashSet::new(); word_list.len()]; + + // 构建邻接矩阵 + for i in 0..word_list.len() { + for j in 1..word_list.len() { + if Self::is_one_char(&word_list[i], &word_list[j]) { + neighbors[i].insert(j); + neighbors[j].insert(i); + } + } + + if word_list[i] == end_word { + end_found = true; + end = i; + } + + if !start_found { + if Self::is_one_char(&word_list[i], &begin_word) { + start = i; + } + } + } + + if !end_found { + return 0; + } + + // 如果开始和结束相同 + // 则必然是跳了一步 + if start == end { + return 2; + } + + let mut result = i32::MAX; + + // dijkstra算法 + let mut distances = vec![i32::MAX; word_list.len()]; + let mut flags = vec![false; word_list.len()]; + distances[start] = 0; + flags[start] = true; + for &i in &neighbors[start] { + distances[i] = 1; + } + + for _ in 1..word_list.len() { + let mut min = i32::MAX; + let mut next = 0; + + for i in 0..word_list.len() { + if !flags[i] && distances[i] < min { + min = distances[i]; + next = i; + } + } + + flags[next] = true; + if next == end { + result = result.min(min); + break; + } + + for &i in &neighbors[next] { + if !flags[i] && min + 1 < distances[i] { + distances[i] = min + 1; + } + } + } + + if result == i32::MAX { + 0 + } else { + if start_found { + result + 1 + } else { + result + 2 + } + } + } + + fn is_one_char(a: &String, b: &String) -> bool { + let mut one_char = false; + + for (c1, c2) in a.chars().zip(b.chars()) { + if c1 != c2 { + if one_char { + return false; + } else { + one_char = true + } + } + } + + one_char + } +} + +// submission codes end + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_127() { + assert_eq!( + Solution::ladder_length( + "hit".to_owned(), + "cog".to_owned(), + vec![ + "hot".to_owned(), + "dot".to_owned(), + "dog".to_owned(), + "lot".to_owned(), + "log".to_owned(), + "cog".to_owned() + ] + ), + 5 + ); + + assert_eq!(Solution::ladder_length("a".to_owned(), "c".to_owned(), vec![ + "a".to_owned(), + "b".to_owned(), + "c".to_owned() + ]), 2); + + assert_eq!(Solution::ladder_length("a".to_owned(), "c".to_owned(), vec![ + "c".to_owned() + ]), 2); + + assert_eq!( + Solution::ladder_length( + "hot".to_owned(), + "dog".to_owned(), + vec![ + "hot".to_owned(), + "dot".to_owned(), + "dog".to_owned(), + ] + ), + 3 + ); + + assert_eq!(Solution::ladder_length("lost".to_owned(), "cost".to_owned(), vec![ + "most".to_owned(), + "fist".to_owned(), + "lost".to_owned(), + "cost".to_owned(), + "fish".to_owned() + ]), 2); + } +}