Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord 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.
给一个单词字典,把一个起始单词变为结束单词,每次只能变化一个字符,而且变化的中间词都在字典中,找出所有最短路径变换的组合。127. Word Ladder的拓展,127题是让返回最短路径的长度。
解法:BFS,这题难度和代码量都很大。
因为要记录路径, 所以要为每一个结点记录一下父结点, 这样最后的时候我们就从终点开始一个个往前找其父结点, 直到找到起始点, 然后翻转一下加入结果集合中即可.
大概过程差不多, 但是有点不同的是当将字典中的一个字符串删除的时候在另一条路径上可能还会用到这个字符. 也就是像这样:
A -> C -> D, B->C->D
他们都会经过C, 并且两个都是最短的路径, 在A的时候搜索到C, 并且将C从字典中删除, 当B在搜索与其距离为1的字符串时, C已经不在字典中了, 那么怎么办呢? 我们设置一个hash表用来存储一个字符串的父结点集合, 这样C不在字典中再去查hash表看C是否在hash表中, 如果在的话并且C的父结点层次和B一样, 那么就将B也加入到C的父结点结合中去. 可以知道, 一个字符串的父结点集合的距离起点的距离必然是相等的, 也就是说他们都是最短距离.
最后遍历完所有的点之后, 再用DFS从终点往前找出所有集合即可.
Java:
public class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
HashSet<String> dict = new HashSet<>(wordList);
HashMap<String, Integer> distance = new HashMap<>();
HashMap<String, List<String>> adj = new HashMap<>();
List<List<String>> result = new ArrayList<>();
List<String> path = new ArrayList<>();
dict.add(beginWord);
bfs(beginWord, endWord, dict, adj, distance);
path.add(beginWord);
dfs(beginWord, endWord, result, path, dict, adj, distance);
return result;
} public List<String> getNeighbor(String word, HashSet<String> dict) {
List<String> result = new ArrayList<>();
char[] arr = word.toCharArray();
for (char c = 'a'; c <= 'z'; c++) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == c) {
continue;
}
char ch = arr[i];
arr[i] = c;
if (dict.contains(String.valueOf(arr))) {
result.add(String.valueOf(arr));
}
arr[i] = ch;
}
}
return result;
} public void bfs(String start,
String end,
HashSet<String> dict,
HashMap<String, List<String>> adj,
HashMap<String, Integer> distance) { for (String word : dict) {
adj.put(word, new ArrayList<String>());
} Queue<String> queue = new LinkedList<>();
queue.offer(start);
distance.put(start, 0);
while (!queue.isEmpty()) {
String curr = queue.poll();
int level = distance.get(curr);
List<String> neighbor = getNeighbor(curr, dict);
for (String nei : neighbor) {
adj.get(curr).add(nei);
if (!distance.containsKey(nei)) {
distance.put(nei, level + 1);
if (!end.equals(nei)) {
queue.offer(nei);
}
}
}
}
} public void dfs(String curr,
String end,
List<List<String>> result,
List<String> path,
HashSet<String> dict,
HashMap<String, List<String>> adj,
HashMap<String, Integer> distance) { if (curr.equals(end)) {
result.add(new ArrayList<>(path));
return;
} for (String nei : adj.get(curr)) {
path.add(nei);
if (distance.containsKey(nei) && distance.get(nei) == distance.get(curr) + 1) {
dfs(nei, end, result, path, dict, adj, distance);
}
path.remove(path.size() - 1);
}
}
}
Java:
class WordNode{
String word;
int numSteps;
WordNode pre; public WordNode(String word, int numSteps, WordNode pre){
this.word = word;
this.numSteps = numSteps;
this.pre = pre;
}
} public class Solution {
public List<List<String>> findLadders(String start, String end, Set<String> dict) {
List<List<String>> result = new ArrayList<List<String>>(); LinkedList<WordNode> queue = new LinkedList<WordNode>();
queue.add(new WordNode(start, 1, null)); dict.add(end); int minStep = 0; HashSet<String> visited = new HashSet<String>();
HashSet<String> unvisited = new HashSet<String>();
unvisited.addAll(dict); int preNumSteps = 0; while(!queue.isEmpty()){
WordNode top = queue.remove();
String word = top.word;
int currNumSteps = top.numSteps; if(word.equals(end)){
if(minStep == 0){
minStep = top.numSteps;
} if(top.numSteps == minStep && minStep !=0){
//nothing
ArrayList<String> t = new ArrayList<String>();
t.add(top.word);
while(top.pre !=null){
t.add(0, top.pre.word);
top = top.pre;
}
result.add(t);
continue;
} } if(preNumSteps < currNumSteps){
unvisited.removeAll(visited);
} preNumSteps = currNumSteps; char[] arr = word.toCharArray();
for(int i=0; i<arr.length; i++){
for(char c='a'; c<='z'; c++){
char temp = arr[i];
if(arr[i]!=c){
arr[i]=c;
} String newWord = new String(arr);
if(unvisited.contains(newWord)){
queue.add(new WordNode(newWord, top.numSteps+1, top));
visited.add(newWord);
} arr[i]=temp;
}
} } return result;
}
}
Python:
class Solution:
# @param start, a string
# @param end, a string
# @param dict, a set of string
# @return an integer
def findLadders(self, start, end, dict):
dict.add(start)
dict.add(end) result, cur, visited, found, trace = [], [start], set([start]), False, {word: [] for word in dict} while cur and not found:
for word in cur:
visited.add(word) next = set()
for word in cur:
for i in xrange(len(word)):
for j in 'abcdefghijklmnopqrstuvwxyz':
candidate = word[:i] + j + word[i + 1:]
if candidate not in visited and candidate in dict:
if candidate == end:
found = True
next.add(candidate)
trace[candidate].append(word)
cur = next if found:
self.backtrack(result, trace, [], end) return result def backtrack(self, result, trace, path, word):
if not trace[word]:
result.append([word] + path)
else:
for prev in trace[word]:
self.backtrack(result, trace, [word] + path, prev)
C++:
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
vector<vector<string>> res;
unordered_set<string> dict(wordList.begin(), wordList.end());
vector<string> p{beginWord};
queue<vector<string>> paths;
paths.push(p);
int level = 1, minLevel = INT_MAX;
unordered_set<string> words;
while (!paths.empty()) {
auto t = paths.front(); paths.pop();
if (t.size() > level) {
for (string w : words) dict.erase(w);
words.clear();
level = t.size();
if (level > minLevel) break;
}
string last = t.back();
for (int i = 0; i < last.size(); ++i) {
string newLast = last;
for (char ch = 'a'; ch <= 'z'; ++ch) {
newLast[i] = ch;
if (!dict.count(newLast)) continue;
words.insert(newLast);
vector<string> nextPath = t;
nextPath.push_back(newLast);
if (newLast == endWord) {
res.push_back(nextPath);
minLevel = level;
} else paths.push(nextPath);
}
}
}
return res;
}
};
类似题目:
[LeetCode] 127. Word Ladder 单词阶梯