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.
给定两个单词(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" 不在字典中,所以不存在符合要求的转换序列。
解题思路:
一开始我以为是列出所有可能路径,所以想用深度优先搜索来做,后来发现是求所有最短路径,尽管保存了每个字符串的路径深度,但是没有办法保证第一条发现的路径深度就是最短路径深度,而且删掉原来的路径也非常麻烦。但是因为广度优先搜索是层层搜索的,所以可以保证第一条发现的路径深度是最短路径深度,之后的无效路径不用加入就行了。
广度优先搜索模板:
queue<type> q ;
q.push(beginnode);
while(!q.empty())
{
for(int sz = q.size() ; sz > 0 ; sz--)
{
type p = q.front() ;
q.pop() ;
对q做一系列处理 ;
q.push(child_of_q)(终止条件,也就是什么时候不再往queue中加子节点) ;
}
}
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList)
{
unordered_set<string> dict(wordList.begin() , wordList.end()) ;
if(dict.empty() || !dict.count(endWord)) return {} ;
dict.erase(beginWord) ;
dict.erase(endWord) ;
unordered_map<string , int> steps = {{beginWord , 1}} ; // 每个字符串最短的路径深度
unordered_map<string , vector<string> > parents ; //每个字符串的母节点
queue<string> q ; //每层的节点集合 。
q.push(beginWord) ;
bool found = false ;
int step = 0 ;
//将所有有效路径加入图中
while(!q.empty() && !found)
{
step++ ; //层序号
for(int sz = q.size() ; sz > 0 ; sz--) //遍历每层的节点
{
string p = q.front() ;
string w = p ;
q.pop() ;
for(int i = 0 ; i < p.size() ; i++) //遍历每个节点所有子节点
{
char ch = w[i] ;
for(char j = 'a' ; j <= 'z' ; j++)
{
if(j == w[i]) continue ;
w[i] = j ;
if(w == endWord)
{
parents[w].push_back(p) ;
found = true ;
}
else if(steps.count(w) && step < steps[w]) // 当当前层序小于字符串所在层序,该路径才有效,才将当前节点记作其父节点,否则路径无效,不加入图中。
{
parents[w].push_back(p) ;
}
if(!dict.count(w)) continue ;
dict.erase(w) ; //当遍历到该字符串时,将当前节点记作其父节点,从字典中删去,避免陷入环。
q.push(w) ;
steps[w] = steps[p] + 1 ;
parents[w].push_back(p) ;
}
w[i] = ch ;
}
}
}
//从后往前深度优先搜索,将所有可能路径加入ans中。
if(found)
{
vector<string> result = { endWord } ; // 向量初始化
get(endWord , beginWord , parents , result ) ;
}
return ans ;
}
private :
vector<vector<string> > ans ;
void get(string child , string beginWord , unordered_map<string , vector<string> >parents , vector<string> result )
{
for(string s : parents[child])
{
if(s == beginWord)
{
result.push_back(s) ;
ans.push_back(vector<string>(result.rbegin() , result.rend())) ;
return ;
}
else
{
result.push_back(s) ;
get(s , beginWord , parents , result) ;
result.pop_back() ;
}
}
}
};
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList)
{
unordered_set<string> dict(wordList.begin() , wordList.end()) ;
if(!dict.count(endWord)) return {} ;
dict.erase(beginWord) ;
dict.erase(endWord) ;
unordered_map<string , int> steps = {{beginWord , 1}} ;
unordered_map<string , vector<string>> parents ;
queue<string> q ;
q.push(beginWord) ;
int step = 0 ;
bool found = false ;
while(!q.empty())
{
step++ ;
cout<<"step:"<<step<<endl;
//cout<<"qz:"<<q.size()<<endl;
for(int i = q.size() - 1 ; i >= 0 ; --i)
{
cout<<"i:"<<i<<endl;
string s = q.front() , w = s ;
q.pop() ;
for(int j = 0 ; j < s.size() ; ++j)
{
//cout<<"j:"<<j<<endl;
char ch = w[j] ;
for(char k = 'a' ; k <= 'z' ; ++k)
{
if(k == ch) continue ;
//cout<<"k:"<<k<<endl;
w[j] = k ;
if(w == endWord)
{
//steps[endWord] = step + 1 ;
parents[endWord].push_back(s) ;
found = true ;
break ;
}
else if(steps.count(w) && step < steps[w])
{
parents[w].push_back(s) ;
}
if(!dict.count(w)) continue;
dict.erase(w) ;
q.push(w) ;
parents[w].push_back(s) ;
steps[w] = step + 1 ;
}
w[j] = ch ;
//if(found) break ;//这里不能加found的判断,因为如果同一层的下一个节点第一个字母无法变出dict中的字符串时,会直接跳出,不会去改变第二个字母;"red","tax",["ted","tex","red","tax","tad","den","rex","pee"]
}
}
if(found) break ;
}
//cout<<found<<endl;
vector<string> result = {endWord} ;
if(found) get(endWord , beginWord , parents , result) ;
return res ;
}
private :
vector<vector<string> > res ;
void get(string endWord , string beginWord , unordered_map<string , vector<string>>& parents , vector<string> result)
{
for(string s : parents[endWord])
{
result.push_back(s) ;
if(s == beginWord)
{
res.push_back(vector<string>(result.rbegin() , result.rend())) ;
return ;
}
get(s , beginWord , parents , result) ;
result.pop_back() ;
}
}
};