bfs + dfs
-
Use BFS to: Build a graph of valid paths using a
parent map
- Note1: node could have many parent, so we use
stepMap
, and explore by level. e.g. - Note2: going back to visited node will not be shortest path. e.g.
- Note1: node could have many parent, so we use
-
Use DFS (backtracking) to: Reconstruct all paths from
endWord
back tobeginWord
using theparent map
-
BFS Time = O(N × L × 26) = O(N × L)
-
DFS Time = O(M × K)
-
M = total number of shortest transformation sequences K = average length of each sequence (at most N)
class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
Set<String> set = new HashSet<>(wordList);
// BFS queue
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
// <word, lists of parent> stores words it transformed from
Map<String, List<String>> parent = new HashMap<>();
// for each visited word, record BFS level
Map<String, Integer> stepMap = new HashMap<>();
stepMap.put(beginWord, 0);
int step = 1;
boolean found = false;
while (!queue.isEmpty()) {
int size = queue.size();
// for each step(bfs layer)
for (int s = 0; s < size; s++) {
String word = queue.poll();
for (int i = 0; i < word.length(); i++) {
char[] arr = word.toCharArray();
for (char j = 'a'; j <= 'z'; j++) {
arr[i] = j;
String newWord = String.valueOf(arr);
// ? already seen newWord at this level (step)
// ? then the current word is another shortest-path parent of newWord
if (stepMap.containsKey(newWord) && step == stepMap.get(newWord)) {
parent.get(newWord).add(word);
}
// generated substitute exists in wordList
if (set.contains(newWord) ) {
queue.offer(newWord);
// ? Prevent further visits (we only want shortest paths)
set.remove(newWord);
parent.putIfAbsent(newWord, new ArrayList<>());
parent.get(newWord).add(word);
// explored at this step
stepMap.put(newWord, step);
if (newWord.equals(endWord)) {
found = true;// break after this step
}
}
}
}
}
step++;
if (found) {
break;
}
}
// use dfs backtrack to collect all path
List<List<String>> res = new ArrayList<>();
if (found) {
Deque<String> path = new LinkedList<>();
path.add(endWord);
dfs(parent, endWord, beginWord, path, res);
}
return res;
}
// DFS from end to start, find all the paths
public void dfs(Map<String, List<String>> parent, String cur, String beginWord, Deque<String> path, List<List<String>> res) {
if (cur.equals(beginWord)) {
res.add(new ArrayList<>(path));
return;
}
for (String prev : parent.get(cur)) {
path.addFirst(prev);
dfs(parent, prev, beginWord, path, res);
path.removeFirst();
}
}
}