126. Word Ladder II (Hard)
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.
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]
Return
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
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.
UPDATE (2017/1/20): The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.
Solution 1: BFS 482ms
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> dict, words;
for (string& w: wordList) dict.insert(w);
if (!dict.count(endWord)) return {};
vector<string> p; p.push_back(beginWord);
queue<vector<string>> paths;
paths.push(p);
vector<vector<string>> res;
int level = 1, minlevel = INT_MAX;
while (!paths.empty()) {
vector<string> path = paths.front(); paths.pop();
if (path.size() > level) {
// delete all the words in prev level
for (string w: words) dict.erase(w);
words.clear();
level = path.size();
if (level > minlevel) break;
}
string last = path.back();
for (int i = 0; i < last.size(); ++i) {
string newLast = last;
for (char c = 'a'; c <= 'z'; ++c) {
newLast[i] = c;
if (dict.count(newLast)) {
words.insert(newLast);
vector<string> nextPath = path;
nextPath.push_back(newLast);
if (newLast == endWord) {
res.push_back(nextPath);
minlevel = level;
} else paths.push(nextPath);
}
}
}
}
return res;
}
};
Solution 2: BFS+DFS 135ms
class Solution {
void dfs(unordered_map<string,vector<string>>& next, string& end, string prev, vector<string>& path, vector<vector<string>>& res) {
if (prev == end) { res.push_back(path); return;}
for (auto& cur: next[prev]) {
path.push_back(cur);
dfs(next, end, cur, path, res);
path.pop_back();
}
}
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> dict;
for (auto& w: wordList) dict.insert(w);
if (!dict.count(endWord)) return {};
unordered_map<string,vector<string>> next; // Neighbors for every node
unordered_map<string,int> dist; // Distance of every node from the start node
queue<string> q; q.push(beginWord);
dist[beginWord] = 0;
int n = beginWord.size();
while (!q.empty()) {
int cnt = q.size();
bool foundEnd = false;
for (int i = 0; i < cnt; ++i) { // curlevel
string curWord = q.front(); q.pop();
int curStep = dist[curWord]+1;
dict.erase(curWord);
bool foundEndwithCurWord = false;
for (int j = 0; j < n; ++j) {
string newWord = curWord;
for (char c = 'a'; c <= 'z'; ++c) {
newWord[j] = c;
if (dict.count(newWord)) {
if (!dist.count(newWord) || dist[newWord] == curStep)
next[curWord].push_back(newWord);
// found end -> shortest path, no need to loop the rest
if (curWord == endWord) {
foundEndwithCurWord = true;
foundEnd = true;
next[curWord] = {endWord};
break;
}
if (!dist.count(newWord)) { // internal node: Check if visited
dist[newWord] = curStep;
q.push(newWord);
}
}
}
if (foundEndwithCurWord) break;
}
}
if (foundEnd) break;
}
/*
for (auto& a: next) {
cout << a.first << ": ";
for (auto& b: a.second) {
cout << b << ", ";
}
cout << endl;
}*/
vector<vector<string>> res;
vector<string> path = {beginWord};
dfs(next, endWord, beginWord, path, res);
return res;
}
};
Solution 3: 2 End BFS
In order to reduce the running time, we should use two-end BFS to slove the problem. 88ms! Accepted c++ solution with two-end BFS. 68ms for Word Ladder and 88ms for Word Ladder II