Skip to content

Commit 79a5acf

Browse files
committed
'Word Ladder II' soln
1 parent a0b9cf6 commit 79a5acf

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

leetcode/word_ladder_ii.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
///
2+
/// Problem: Word Ladder II
3+
///
4+
/// A transformation sequence from word beginWord to word endWord using a dictionary wordList is a
5+
/// sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:
6+
///
7+
/// - Every adjacent pair of words differs by a single letter.
8+
/// - Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.
9+
/// - sk == endWord
10+
///
11+
/// Given two words, beginWord and endWord, and a dictionary wordList, return all the shortest
12+
/// transformation sequences from beginWord to endWord, or an empty list if no such sequence exists.
13+
/// Each sequence should be returned as a list of the words [beginWord, s1, s2, ..., sk].
14+
///
15+
/// Example 1:
16+
/// Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
17+
/// Output: [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
18+
/// Explanation: There are 2 shortest transformation sequences:
19+
/// "hit" -> "hot" -> "dot" -> "dog" -> "cog"
20+
/// "hit" -> "hot" -> "lot" -> "log" -> "cog"
21+
///
22+
/// Example 2:
23+
/// Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
24+
/// Output: []
25+
/// Explanation: The endWord "cog" is not in wordList, so there is no valid transformation sequence.
26+
///
27+
/// Constraints:
28+
/// 1 <= beginWord.length <= 5
29+
/// endWord.length == beginWord.length
30+
/// 1 <= wordList.length <= 500
31+
/// wordList[i].length == beginWord.length
32+
/// beginWord, endWord, and wordList[i] consist of lowercase English letters.
33+
/// beginWord != endWord
34+
/// All the words in wordList are unique.
35+
///
36+
37+
// # Solution
38+
// Time complexity: O(N * M^2) where N is the length of wordList and M is the length of each word
39+
// Space complexity: O(N^2)
40+
41+
use std::collections::{HashSet, HashMap, VecDeque};
42+
impl Solution {
43+
pub fn find_ladders(begin_word: String, end_word: String, word_list: Vec<String>) -> Vec<Vec<String>> {
44+
let mut word_set: HashSet<String> = word_list.into_iter().collect();
45+
46+
47+
if !word_set.contains(&end_word) {
48+
return vec![];
49+
}
50+
51+
word_set.remove(&begin_word);
52+
53+
let mut queue = VecDeque::new();
54+
queue.push_back(begin_word.clone());
55+
56+
let mut parents: HashMap<String, Vec<String>> = HashMap::new();
57+
58+
let mut current_level = HashSet::new();
59+
current_level.insert(begin_word.clone());
60+
61+
let mut found = false;
62+
63+
while !queue.is_empty() && !found {
64+
let level_size = queue.len();
65+
66+
let mut level_visited = HashSet::new();
67+
68+
for _ in 0..level_size {
69+
let current_word = queue.pop_front().unwrap();
70+
71+
let word_chars: Vec<char> = current_word.chars().collect();
72+
73+
for i in 0..word_chars.len() {
74+
let mut new_chars = word_chars.clone();
75+
76+
for c in b'a'..=b'z' {
77+
new_chars[i] = c as char;
78+
let new_word: String = new_chars.iter().collect();
79+
80+
if !word_set.contains(&new_word) {
81+
continue;
82+
}
83+
84+
parents.entry(new_word.clone())
85+
.or_insert_with(Vec::new)
86+
.push(current_word.clone());
87+
88+
if !level_visited.contains(&new_word) {
89+
level_visited.insert(new_word.clone());
90+
91+
if new_word == end_word {
92+
found = true;
93+
} else {
94+
queue.push_back(new_word);
95+
}
96+
}
97+
}
98+
}
99+
}
100+
101+
for word in &level_visited {
102+
word_set.remove(word);
103+
}
104+
}
105+
106+
if !found {
107+
return vec![];
108+
}
109+
110+
let mut result = Vec::new();
111+
let mut path = vec![end_word.clone()];
112+
113+
Self::dfs(&end_word, &begin_word, &parents, &mut path, &mut result);
114+
115+
result
116+
}
117+
118+
fn dfs(word: &str, begin_word: &str, parents: &HashMap<String, Vec<String>>,
119+
path: &mut Vec<String>, result: &mut Vec<Vec<String>>) {
120+
if word == begin_word {
121+
let mut complete_path = path.clone();
122+
complete_path.reverse();
123+
result.push(complete_path);
124+
return;
125+
}
126+
127+
if let Some(parent_words) = parents.get(word) {
128+
for parent in parent_words {
129+
path.push(parent.clone());
130+
Self::dfs(parent, begin_word, parents, path, result);
131+
path.pop();
132+
}
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)