题目
按字典 wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> … -> sk 这样的单词序列,并满足:
每对相邻的单词之间仅有单个字母不同。
转换过程中的每个单词 si(1 <= i <= k)必须是字典 wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
sk == endWord
给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord, s1, s2, …, sk] 的形式返回。
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
vector<vector<string>> result;//存储最终的所有最短路径
queue<vector<string>> que;//队列辅助实现BFS
unordered_set<string> s(wordList.begin(), wordList.end());//哈希表,如果已经访问过此节点则删除
que.push({beginWord});//将初始节点入队列
//层序遍历,访问每一个节点,即每一个可能的路径
while(!que.empty()){
unordered_set<string> visited;//存储该层访问过的单词
//当前队列中存储的为该层的所有节点,遍历所有节点
//每一个节点都是一条路径
for(int i=que.size(); i>0; --i){
auto cur=que.front();//取出当前的队首节点
que.pop();
auto tail=cur.back();//节点为路径,它的最后一个单词影响下一层的结果
//如果该单词为endword,表示已经找到一条最短路径
if(tail==endWord){
result.push_back(cur);
continue;//要在遍历完该层后才能返回所有结果
}
//从当前节点查找下一层节点
//对当前的单词,遍历它的每一个字母,对每一个字母都尝试26个字母中的每一种情况,在哈希表中查找是否存在
for(int j=0; j<tail.size(); ++j){
char tmp=tail[j];//记录初始的单词
for(char c='a'; c<='z'; ++c){
if(tmp==c) continue;//如果为初始单词跳过当前循环
tail[j]=c;
if(s.count(tail)){
//找到一种情况,将新找到的单词加入到visited中,表示已经访问,给当前的路径添加新的单词,把新的路径入队,再将路径恢复成初始的状态,尝试下一种情况
visited.insert(tail);
cur.push_back(tail);
que.push(cur);
cur.pop_back();
}
}
tail[j]=tmp;//恢复初始的单词
}
}
//如果当前层遍历结束后,result不是空,则表示已经找到了最短路径,返回它
if(result.size()) return result;
//否则,将该层已经访问过的单词从可能的结果中删掉,遍历下一层
for(auto &word:visited) s.erase(word);
}
return {};//如果队列为空依然没有找到路径,则表示没有符合题意的转换路径
}
};
- 思路
- 见代码
- 为什么最短路径上前面已经出现过的元素不会在下一层出现
- 两个不同层次但是相同字符串的结点后面的结构并不完全一样,因为它们前面走过的路径,经历过的结点不同, 剩余的可供选择的结点也不一样,因此后面的结构是不一样的。但是可以证明,下面层次的结点如果有到终点的路径,那它一定不是最短 的,故可以忽律掉它。证明如下:
- A与B是出现在树里不同节点的相同字符串,A在上层,B在下
- B字符串(黄色)经过若干步到达目标。这里有两种情况:
2.1 这若干个字符串并没有包含A以前的元素,这样A后面必定存在与B后面一样的路径
2.2 这若干个元素包含有A以前的元素,这样A以前的某个也存在于B后面的元素之后必 定存在与它在B的位置的后面一样的路径 - 综上所述,无论哪种情况,B后面都不会是最短路径,故可以忽律
- 两个不同层次但是相同字符串的结点后面的结构并不完全一样,因为它们前面走过的路径,经历过的结点不同, 剩余的可供选择的结点也不一样,因此后面的结构是不一样的。但是可以证明,下面层次的结点如果有到终点的路径,那它一定不是最短 的,故可以忽律掉它。证明如下:
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
vector<vector<string>> result;
queue<vector<string>> que;
unordered_set<string> s(wordList.begin(),wordList.end());
que.push({beginWord});
while(!que.empty())
{
unordered_set<string> visited;
for(int i=que.size();i>0;i--)
{
vector<string> cur=que.front();
que.pop();
string tail=cur.back();
if(tail==endWord)
{
result.push_back(cur);
continue;
}
for(int j=0;j<tail.size();j++)
{
string tmp=tail;
for(char c='a';c<='z';c++)
{
tmp[j]=c;
//if(tmp==tail) continue;
if(s.count(tmp))
{
visited.insert(tmp);
cur.push_back(tmp);
que.push(cur);
cur.pop_back();
}
}
}
}
if(!result.empty()) return result;
for(auto &word:visited) s.erase(word);
}
return {};
}
};