题目描述
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:
- 每次转换只能改变一个字母。
- 转换过程中的中间单词必须是字典中的单词。
说明
- 如果不存在这样的转换序列,返回一个空列表。
- 所有单词具有相同的长度。
- 所有单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
样例1
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
样例2
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: []
解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。
算法
(BFS+DFS) $O(2^nnL)$
与 LeetCode 127. Word Ladder 类似,我们可以开一个哈希表dist
,然后用宽搜来记录从起点beginWord
到其他单词之间的距离。BFS结束后如果发现从起点beginWord
无法到达终点endWord
我们可以直接返回。否则我们从终点endWord
开始进行DFS来找到所有路径,每次找到与当前点距离为1的点,即可以互相转换的点,搜索到起点beginWord
为止。
注意这里要从终点开始往起点搜,以减少搜索空间,若从起点开始则有可能搜索到很多无效路径即走不到终点的路径,而从终点开始每条路径是一定可以走到起点的。
时间复杂度
从起点开始宽搜的时间复杂度为 $O(nL^2)$,而从终点开始深搜的时间复杂度与方案数有关。方案数最高是 $O(2^n)$ 级别的,考虑从终点开始第一层有两种选择(两个节点),然后第二层有一种选择,之后第三层又是两种选择,以此类推,总方案数为 $2^{\frac{n - 1}{3}}$。而记录每个方案需要 $O(nL)$ 的复杂度。因此总时间复杂度为 $O(2^nnL)$。
C++ 代码
class Solution {
public:
string beginWord;
unordered_map<string, int> dist;
unordered_set<string> hash;
vector<vector<string>> res;
vector<string> path;
vector<vector<string>> findLadders(string _beginWord, string endWord, vector<string>& wordList) {
hash = unordered_set<string>(wordList.begin(), wordList.end());
if (!hash.count(endWord)) return res;
beginWord = _beginWord;
queue<string> q;
q.push(beginWord);
dist[beginWord] = 1;
while (q.size()) {
auto t = q.front();
q.pop();
string s = t;
for (int i = 0; i < t.size(); i ++ ) {
char c = t[i];
for (int j = 0; j < 26; j ++ ) {
t[i] = 'a' + j;
if (hash.count(t) && !dist[t]) {
dist[t] = dist[s] + 1;
q.push(t);
}
}
t[i] = c;
}
}
if (!dist[endWord]) return res;
hash.insert(beginWord);
path.push_back(endWord);
dfs(endWord);
path.pop_back();
return res;
}
void dfs(string s) {
if (s == beginWord) {
res.push_back(vector<string>(path.rbegin(), path.rend()));
return;
}
string tmp = s;
for (int i = 0; i < s.size(); i ++ ) {
char c = s[i];
for (int j = 0; j < 26; j ++ ) {
s[i] = 'a' + j;
if (dist[s] == dist[tmp] - 1 && hash.count(s)) {
path.push_back(s);
dfs(s);
path.pop_back();
}
}
s[i] = c;
}
}
};
大佬,这道题的时间复杂度应该是$O(26nL^2)$吧,单词数量是n,单词长度是L,最后用哈希表查询是$O(L)$,所以总的是$O(26nL^2)$。
究极班讲了这道题,我看大佬的做法和y总差不多hh。
这里方案数可能是指数级别的。
大佬,为什么一开始
dist[beginWord] = 1;
?这里
dist
还起到判重的作用,每个点的距离只会被更新一次。如果dist[beginWord] = 0
那么从beginWord
开始宽搜的时候可能会再次枚举到beginWord
并且将它的距离更新为1
,这样在更新前得到的其他点的距离就是错的。