Skip to content

Commit 65bb562

Browse files
authored
Merge pull request #697 from mahek0620/word-ladder
Word ladder and word ladder II
2 parents 7e805dc + 447f656 commit 65bb562

File tree

2 files changed

+531
-0
lines changed

2 files changed

+531
-0
lines changed
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
---
2+
id: word-ladder-II
3+
title: Word ladder II solution
4+
sidebar_label: 0126 Word ladder II
5+
tags:
6+
- String
7+
- BFS (Breadth-First Search)
8+
- Backtracking
9+
- Graph
10+
- LeetCode
11+
- Python
12+
- Java
13+
- C++
14+
description: "This is a solution to the word ladder II problem on LeetCode."
15+
---
16+
17+
## Problem Description
18+
19+
A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord $s1 -> s2 -> ... -> sk$ such that:
20+
21+
Every adjacent pair of words differs by a single letter.
22+
Every si for $1 <= i <= k$ is in wordList. Note that beginWord does not need to be in wordList.
23+
sk == endWord
24+
Given two words, beginWord and endWord, and a dictionary wordList, return all the shortest transformation sequences from beginWord to endWord, or an empty list if no such sequence exists. Each sequence should be returned as a list of the words [beginWord, s1, s2, ..., sk].
25+
26+
### Examples
27+
28+
**Example 1:**
29+
30+
```
31+
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
32+
Output: [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
33+
Explanation: There are 2 shortest transformation sequences:
34+
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
35+
"hit" -> "hot" -> "lot" -> "log" -> "cog"
36+
```
37+
38+
**Example 2:**
39+
40+
```
41+
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
42+
Output: []
43+
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.
44+
```
45+
46+
### Constraints
47+
48+
- $1 <= beginWord.length <= 5$
49+
- $endWord.length == beginWord.length$
50+
- $1 <= wordList.length <= 500$
51+
- $wordList[i].length == beginWord.length$
52+
- beginWord, endWord, and wordList[i] consist of lowercase English letters.
53+
- beginWord != endWord
54+
- All the words in wordList are unique.
55+
- The sum of all shortest transformation sequences does not exceed 105.
56+
57+
## Solution for Word Ladder II Problem
58+
59+
<Tabs>
60+
<TabItem value="Python 3" label="Python 3">
61+
62+
### Approach :
63+
64+
#### Intuition
65+
66+
Define a helper function neighbors(word) that generates all the possible words by changing a single character in the given word.
67+
Initialize a variable words as a dictionary with beginWord as the key and a lambda function returning [[beginWord]] as the value. This dictionary will keep track of all possible transformation sequences.
68+
Initialize a set unvisited containing all words in wordList except beginWord.
69+
Perform BFS:
70+
While there are words in words and endWord is not yet found in words:
71+
Increment a counter i.
72+
Initialize a new dictionary new_words to store the next layer of words.
73+
Iterate through each word s in words:
74+
Generate all possible neighbors ss of s.
75+
If ss is in unvisited, create a lambda function get_seqs that appends ss to each sequence of words ending with s, and add ss and get_seqs to new_words.
76+
Update words to new_words.
77+
Remove the keys of new_words from unvisited.
78+
Return the transformation sequences ending with endWord.
79+
80+
81+
#### Code in Different Languages
82+
83+
<Tabs>
84+
<TabItem value="Python3" label="Python3">
85+
<SolutionAuthor name="@mahek0620"/>
86+
```python3
87+
class Solution:
88+
def findLadders(
89+
self, beginWord: str, endWord: str, wordList: List[str]
90+
) -> List[List[str]]:
91+
def neighbors(word):
92+
for i in range(len(word)):
93+
for char in "abcdefghijklmnopqrstuvwxyz":
94+
yield word[:i] + char + word[i + 1 :]
95+
96+
i = 1
97+
words = {beginWord: lambda: [[beginWord]]}
98+
unvisited = set(wordList)
99+
while words and endWord not in words:
100+
i += 1
101+
new_words = defaultdict(lambda: lambda: [])
102+
for s in words:
103+
for ss in neighbors(s):
104+
if ss in unvisited:
105+
106+
def get_seqs(capture=(ss, new_words[ss], words[s])):
107+
ss, ss_get_seqs, s_get_seqs = capture
108+
seqs = ss_get_seqs()
109+
for seq in s_get_seqs():
110+
seq.append(ss)
111+
seqs.append(seq)
112+
return seqs
113+
114+
new_words[ss] = get_seqs
115+
words = new_words
116+
unvisited -= words.keys()
117+
return words[endWord]()
118+
119+
```
120+
121+
</TabItem>
122+
<TabItem value="Java" label="Java">
123+
<SolutionAuthor name="@mahek0620"/>
124+
```java
125+
class Solution {
126+
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
127+
List<List<String>> ans = new ArrayList<>();
128+
Map<String, Set<String>> reverse = new HashMap<>(); // reverse graph start from endWord
129+
Set<String> wordSet = new HashSet<>(wordList); // remove the duplicate words
130+
wordSet.remove(beginWord); // remove the first word to avoid cycle path
131+
Queue<String> queue = new LinkedList<>(); // store current layer nodes
132+
queue.add(beginWord); // first layer has only beginWord
133+
Set<String> nextLevel = new HashSet<>(); // store nextLayer nodes
134+
boolean findEnd = false; // find endWord flag
135+
while (!queue.isEmpty()) { // traverse current layer nodes
136+
String word = queue.remove();
137+
for (String next : wordSet) {
138+
if (isLadder(word, next)) { // is ladder words
139+
// construct the reverse graph from endWord
140+
Set<String> reverseLadders = reverse.computeIfAbsent(next, k -> new HashSet<>());
141+
reverseLadders.add(word);
142+
if (endWord.equals(next)) {
143+
findEnd = true;
144+
}
145+
nextLevel.add(next); // store next layer nodes
146+
}
147+
}
148+
if (queue.isEmpty()) { // when current layer is all visited
149+
if (findEnd) break; // if find the endWord, then break the while loop
150+
queue.addAll(nextLevel); // add next layer nodes to queue
151+
wordSet.removeAll(nextLevel); // remove all next layer nodes in wordSet
152+
nextLevel.clear();
153+
}
154+
}
155+
if (!findEnd) return ans; // if can't reach endWord from startWord, then return ans.
156+
Set<String> path = new LinkedHashSet<>();
157+
path.add(endWord);
158+
// traverse reverse graph from endWord to beginWord
159+
findPath(endWord, beginWord, reverse, ans, path);
160+
return ans;
161+
}
162+
163+
164+
private void findPath(String endWord, String beginWord, Map<String, Set<String>> graph,
165+
List<List<String>> ans, Set<String> path) {
166+
Set<String> next = graph.get(endWord);
167+
if (next == null) return;
168+
for (String word : next) {
169+
path.add(word);
170+
if (beginWord.equals(word)) {
171+
List<String> shortestPath = new ArrayList<>(path);
172+
Collections.reverse(shortestPath); // reverse words in shortest path
173+
ans.add(shortestPath); // add the shortest path to ans.
174+
} else {
175+
findPath(word, beginWord, graph, ans, path);
176+
}
177+
path.remove(word);
178+
}
179+
}
180+
181+
private boolean isLadder(String s, String t) {
182+
if (s.length() != t.length()) return false;
183+
int diffCount = 0;
184+
int n = s.length();
185+
for (int i = 0; i < n; i++) {
186+
if (s.charAt(i) != t.charAt(i)) diffCount++;
187+
if (diffCount > 1) return false;
188+
}
189+
return diffCount == 1;
190+
}}
191+
```
192+
193+
</TabItem>
194+
<TabItem value="C++" label="C++">
195+
<SolutionAuthor name="@mahek0620"/>
196+
```cpp
197+
class Solution {
198+
public:
199+
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
200+
int n=wordList.size();
201+
202+
unordered_set<string> uset;
203+
for(auto const &it:wordList){
204+
if(it!=beginWord) uset.insert(it);
205+
}
206+
207+
queue<string> q;
208+
q.push(beginWord);
209+
210+
vector<pair<string,int>> levels;
211+
int cnt=0;
212+
bool flag=false;
213+
214+
while(!q.empty()){
215+
int sz=q.size();
216+
217+
for(int i=0;i<sz;i++){
218+
string node=q.front();
219+
q.pop();
220+
221+
levels.push_back({node,cnt});
222+
223+
if(node==endWord){
224+
flag=true;
225+
break;
226+
}
227+
228+
for(int i=0;i<node.length();i++){
229+
char original=node[i];
230+
for(char j='a';j<='z';j++){
231+
if(original==j) continue;
232+
233+
node[i]=j;
234+
if(uset.find(node)!=uset.end()){
235+
q.push(node);
236+
uset.erase(node);
237+
}
238+
}
239+
node[i]=original;
240+
}
241+
}
242+
if(flag) break;
243+
cnt++;
244+
}
245+
246+
vector<vector<string>> ans;
247+
248+
if(!flag){
249+
return ans;
250+
}
251+
252+
vector<pair<string,int>>::reverse_iterator it=levels.rbegin();
253+
254+
vector<string> vec;
255+
dfs(endWord,levels,ans,vec,it,cnt,beginWord);
256+
257+
return ans;
258+
}
259+
260+
private:
261+
262+
void dfs(string str,vector<pair<string,int>> &levels,vector<vector<string>> &ans,vector<string> &vec,vector<pair<string,int>>::reverse_iterator it,int cnt,string &beginWord){
263+
if(str==beginWord){
264+
vec.push_back(str);
265+
reverse(vec.begin(),vec.end());
266+
ans.push_back(vec);
267+
reverse(vec.begin(),vec.end());
268+
vec.pop_back();
269+
270+
return;
271+
}
272+
273+
while(it!=levels.rend() && it->second==cnt){
274+
it++;
275+
}
276+
277+
while(it!=levels.rend() && it->second==cnt-1){
278+
if(isPossible(str,it->first)){
279+
vec.push_back(str);
280+
dfs(it->first,levels,ans,vec,it,cnt-1,beginWord);
281+
vec.pop_back();
282+
}
283+
it++;
284+
}
285+
}
286+
287+
bool isPossible(string str1,string str2){
288+
int n=str1.length();
289+
290+
int diff=0;
291+
for(int i=0;i<n;i++){
292+
if(str1[i]!=str2[i]){
293+
diff++;
294+
}
295+
296+
if(diff>1) return false;
297+
}
298+
299+
return true;
300+
}
301+
};
302+
```
303+
304+
</TabItem>
305+
</Tabs>
306+
307+
308+
</TabItem>
309+
310+
</Tabs>
311+
312+
## References
313+
314+
- **LeetCode Problem**: [Word Ladder II](https://leetcode.com/problems/word-ladder-ii/)
315+
316+
- **Solution Link**: [LeetCode Solution](https://leetcode.com/problems/word-ladder-ii/solution/)
317+
318+
- **Authors GeeksforGeeks Profile:** [Mahek Patel](https://leetcode.com/u/mahekrpatel611/)

0 commit comments

Comments
 (0)