Leetcode.126 单词接龙 II
按字典 wordList
完成从单词 beginWord
到单词 endWord
转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> ... -> sk
这样的单词序列,并满足:
每对相邻的单词之间仅有单个字母不同。
转换过程中的每个单词 si(1<=i<=k)s_i(1 <= i <= k)si(1<=i<=k)必须是字典 wordList 中的单词。注意,beginWord
不必是字典 wordList
中的单词。
sk==endWords_k == endWordsk==endWord
给你两个单词 beginWord
和 endWord
,以及一个字典 wordList
。请你找出并返回所有从 beginWord
到 endWord
的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord,s1,s2,...,sk][beginWord, s_1, s_2, ..., s_k][beginWord,s1,s2,...,sk] 的形式返回。
输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:[[“hit”,“hot”,“dot”,“dog”,“cog”],[“hit”,“hot”,“lot”,“log”,“cog”]]
解释:存在 2 种最短的转换序列:
“hit” -> “hot” -> “dot” -> “dog” -> “cog”
“hit” -> “hot” -> “lot” -> “log” -> “cog”
输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出:[]
解释:endWord “cog” 不在字典 wordList 中,所以不存在符合要求的转换序列。
分析:
本题在 127. 单词接龙 的基础之上,还要记录最短的路径,求所有的最短路径。
一般这种 求一些路径 我们可以使用 回溯 来解决。
如图,我们要求 起点hit 和 终点cog 的最短路径。
思路:
代码:
class Solution {
public://记录 wordList 中的单词unordered_set uset;//记录结果路径vector> res;//最短路径长度int min_d = 1e9;void dfs(string cur,string endWord,vector& path,unordered_set& visited){if(cur == endWord){int len = path.size();if(len < min_d){min_d = len;res.clear();res.push_back(path);}else if(len == min_d) res.push_back(path);return ;}//此时path.size() > min_d,说明此时的path中肯定不是最短路径 直接返回即可if(path.size() > min_d) return ;int n = cur.size();//从 cur 的每一个位置,都从 'a' 到 'z' 枚举for(int i = 0;i < n;i++){char op = cur[i];for(char c = 'a';c <= 'z';c++){//如果 c 与 原字符 op 一样 直接跳过本次循环if(op == c) continue;cur[i] = c;if(uset.count(cur)){//如果已经访问过这个点 也直接跳过本次循环if(visited.count(cur)) continue;//记录新的点visited.insert(cur);path.push_back(cur);dfs(cur,endWord,path,visited);//回溯 恢复现场visited.erase(cur);path.pop_back();}}cur[i] = op;}}vector> findLadders(string beginWord, string endWord, vector& wordList) {unordered_set visited;vector path;for(auto &s:wordList) uset.insert(s);//如果 wordList 不包含 endWord 直接返回空列表if(!uset.count(endWord)) return res;//path 此时先加入起点path.push_back(beginWord);//因为是无向图 所以用一个哈希表来记录 已经访问过的点visited.insert(beginWord);dfs(beginWord,endWord,path,visited);return res;}
};
但是这份代码会超时。。。
解法二:
建好的图应该是以下这个样子:
代码:
class Solution {
public://记录结果vector> res;//图unordered_map> g;vector path;string t;void dfs(string u){if(u == t){//因为从终点到起点 所以我们要逆序加入结果列表res.emplace_back(path.rbegin(),path.rend());return;}for(auto &v:g[u]){path.push_back(v);dfs(v);//回溯 恢复现场path.pop_back();}}vector> findLadders(string beginWord, string endWord, vector& wordList) {//记录从起点开始的长度unordered_map dist;//记录 wordList 的单词unordered_set uset(wordList.begin(),wordList.end());queue q;//起点距离为0dist[beginWord] = 0;q.push(beginWord);while(!q.empty()){auto cur = q.front();q.pop();auto next_str = cur;int n = cur.size();for(int i = 0;i < n;i++){char op = next_str[i];for(char c = 'a';c <= 'z';c++){if(c == op) continue;next_str[i] = c;if((uset.count(next_str)) && (!dist.count(next_str) || dist[next_str] == dist[cur] + 1)){//建反向边g[next_str].push_back(cur);}//点 s 是没有访问过的点if(uset.count(next_str) && !dist.count(next_str)){dist[next_str] = dist[cur] + 1;//如果 next_str 是终点,直接跳出循环if(next_str == endWord) break;q.push(next_str);}}next_str[i] = op;}}//判断从起点能否到达终点 不能直接返回空列表if(!dist.count(endWord)) return res;//path 加入终点 倒着 dfspath.push_back(endWord);t = beginWord;dfs(endWord);return res;}
};