126. Word Ladder II
Given two words (beginWord_and_endWord), and a dictionary's word list, find all shortest transformation sequence(s) frombeginWord_to_endWord, such that:
- Only one letter can be changed at a time
- Each transformed word must exist in the word list. Note that _beginWord _is _not _a transformed word.
Note:
- Return an empty list if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
- You may assume no duplicates in the word list.
- You may assume _beginWord _and _endWord _are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: []
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
Thoughts:
"Naive" Solution: Create a dictionary that maps <str word, List[List[str]]]> Solution lists. Initialize the layer[beginword] word as [[beginword]].
- Expanding the layer list as follows:
- Having a childLayer which maps the layer for the next step
- while current layer is not empty
- for each w in layers keys:
- if w is in the EndWord: res list record the results by taking all the elements of layer[w]
- else for every position, flop the word w as neww. check if neww is in the wordList: add neww after every path in layer[w], add this result into childLayer[neww]
- set layer to newLayer for the next iteration
- remvoe all the keys from wordList that is in newlayers
- Expanding the layer list as follows:
BFS + DFS:
Shortest transformation: BFS to find the shortest path between start and end
Find all the transformation: DFS with backtracking to find all the answer
Code: T:O(L^3*l) ? (L as wordList length, l as word length). S:O(L^3*l)?
class Solution(object):
def findLadders(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordList: List[str]
:rtype: List[List[str]]
"""
wordList = set(wordList)
res,layer = [], {}
layer[beginWord] = [[beginWord]]
while layer:
newlayer = collections.defaultdict(list)
for w in layer:
if w == endWord:
res += [k for k in layer[w]]
else:
for i in range(len(w)):
for c in string.ascii_lowercase:
neww = w[:i] + c + w[i+1:]
if neww in wordList:
newlayer[neww]+= [j + [neww] for j in layer[w]]
wordList -= set(newlayer.keys()) # for avoiding unsupported operation between sets and list
layer = newlayer
return res
Code: BFS + DFS
class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
HashSet<String> dict = new HashSet<>(wordList);
List<List<String>> res = new ArrayList<>();
Map<String, Integer> distance = new HashMap<>();
Map<String, ArrayList<String>> neighbors = new HashMap<>();
ArrayList<String> solution = new ArrayList<>();
dict.add(beginWord);
bfs(beginWord, endWord, dict, distance, neighbors);
dfs(beginWord, endWord, dict, distance, neighbors, solution, res);
return res;
}
private void bfs(String start, String end, Set<String> dict, Map<String, Integer> distance, Map<String, ArrayList<String>> nodeNeighbors) {
for (String str : dict)
nodeNeighbors.put(str, new ArrayList<String>());
Queue<String> queue = new LinkedList<String>();
queue.offer(start);
distance.put(start, 0);
while (!queue.isEmpty()) {
int count = queue.size();
boolean foundEnd = false;
for (int i = 0; i < count; i++) {
String cur = queue.poll();
int curDistance = distance.get(cur);
List<String> neighbors = getNeighbors(cur, dict);
for (String neighbor : neighbors) {
// System.out.println(cur);
nodeNeighbors.get(cur).add(neighbor);
if (!distance.containsKey(neighbor)) {// Check if visited
distance.put(neighbor, curDistance + 1);
if (end.equals(neighbor))// Found the shortest path
foundEnd = true;
else
queue.offer(neighbor);
}
}
}
if (foundEnd)
break;
}
}
private void dfs(String cur, String end, Set<String>dict , Map<String, Integer> distance, Map<String, ArrayList<String>> neighbors, List<String> solution, List<List<String>> res){
solution.add(cur);
// found end: add
if(end.equals(cur)) res.add(new ArrayList<String> (solution));
// not found yet: recursively calling dfs
else{
for(String next: neighbors.get(cur)){
if(distance.get(next) == distance.get(cur) + 1)
dfs(next, end, dict, distance, neighbors, solution, res);
}
}
solution.remove(solution.size() - 1);
}
private List<String> getNeighbors(String node, Set<String> dict){
List<String> res = new ArrayList<>();
char chs [] = node.toCharArray();
for(char ch = 'a'; ch <= 'z'; ch++){
for(int i = 0 ; i < chs.length; i++){
char old = chs[i];
chs[i] = ch;
if(dict.contains(String.valueOf(chs))){
res.add(String.valueOf(chs));
}
chs[i] = old;
}
}
return res;
}
}