鉴于该题改了一晚上加一白天,我决定写篇博客纪念纪念。
Total Accepted: 33992
Total Submissions: 255558
Difficulty: 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 intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Return
[ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
Note:
- All words have the same length.
- All words contain only lowercase alphabetic characters.
首先说说我的思路:
我想着从endword开始进行广度优先遍历,直到在某一层的遍历中遇到beginword。
此处广度优先遍历我采用双队列,一个队列是当前队列,一个是下一层的队列。
因为要构造查询单词的路劲,所以我使用一个map<string,vector<string>>来存放一个单词的先驱。这个地方为什么是
vector<string>呢?因为一个单词不是只有一个前驱。
还要创建一个unordered_map<string, int> strState来表示每一个节点是否被访问过,之所以还有第二个Int元素是因为可能存在上一层两个以上节点指向下一层同一个节点。距离来说,比如上一层的节点是"hot","dot"下一层“lot”的前驱就有两个,那么我上一层处理到hot时候,搜索到下一层要处理的节点是lot,同时lot的前驱是hot。同时当处理dot的时候,下一层的lot与dot只有一个字母不同,但是它标志访问过了,此时有了unordered_map<string, int> 中int,就可以知道该单词的前驱与搜索到的前驱是不是在同一层,在就把当前的作为前驱加入到
前驱举例:
则map<sting,vector>的存储样子是,
(dog,cog)
(log,cog)
(dot,dog)
(lot,log)
(hot,{dot,lot})
(hit,hot)
那么构造这个返回结果就简单了,看代码直接可以懂得
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, unordered_set<string> &wordList)
{
if (beginWord == endWord)
return{};
int n = wordList.size();
wordList.insert(beginWord);//把开始的单词插入到单词set中。
unordered_map<string, vector<string>> fatherString;//记录所有单词的前驱
queue<string> currentque, nextque;//两个队列,一个是遍历的时候当前层的,一个是下一层要遍历的。
unordered_map<string, int> strState;
//状态哈希,如果遍历过则保存进来,注意第二个值表示它所在层的。可能上一层两个节点指向下一层同一个节点
currentque.push(endWord);//首先将末尾压入当前处理的队列中
string temp;
static int level = 1;//记录现在处理第几层单词呢。
int signal = 0;//表示下一层是否搜索到beginword.
while (signal == 0 && !currentque.empty())
{
while (!currentque.empty())
{
temp = currentque.front();
currentque.pop();
for (int i = 0; i < temp.size(); i++)//搜索所有与temp差距一个字母的单词
{
string thestr = temp;
for (char ch = 'a'; ch <= 'z'; ch++)
{
thestr[i] = ch;
if (wordList.find(thestr) != wordList.end() && thestr!=temp)//查询这个与temp一个字母不一样的字符在不在wordlist中
{
if (strState.find(thestr) == strState.end())//在wordlist中,且没有被访问过
{
fatherString[thestr].push_back(temp);//记录前驱
strState[thestr] = level;//表示被访问过了,并且是在第level层被访问的
nextque.push(thestr);//将其加入的下一层遍历的节点中
if (thestr == beginWord)//遇到了beginword,这层就是最后一层。
signal = 1;
}
else if (level == strState[thestr])//在wordlist中,但是被访问过,需要查询前驱与当前的是不是一层。
fatherString[thestr].push_back(temp);//在同一层,则temp也是该字符的前驱
}
}
}
}
level++;//一层遍历完了,下一层
std::swap(currentque, nextque);//此时currentque为空了,nextque存放下一层待遍历的,所以交换。
}
if (signal == 0)//表示遍历了所有元素,但是没发现beginword
return{};
else
{
return buildPath(fatherString, beginWord, endWord);//根据前驱构造结果。
}
}
vector<vector<string>> buildPath(unordered_map<string, vector<string>> father, string beginWord, string endWord)
{
vector<vector<string>> res;
vector<vector<string>> restemp;
vector<string> temp;
string pre;
int signal = 0;//表示构造到了endword.
//首先将beginword的前驱到beginword的所有可能构造出来.
for (int i = 0; i < father[beginWord].size(); i++)
{
temp.push_back(beginWord);
temp.push_back(father[beginWord][i]);
if (father[beginWord][i] == endWord)
signal = 1;
res.push_back(temp);
temp.clear();
}
while (signal == 0)
{
for (int i = 0; i < res.size(); i++)
{
temp = res[i];
for (int j = 0; j < father[res[i][res[i].size() - 1]].size(); j++)
{
pre = father[res[i][res[i].size() - 1]][j];
if (pre == endWord)
signal = 1;
temp.push_back(pre);
restemp.push_back(temp);
temp.pop_back();
}
}
res = restemp;
restemp.clear();
}
return res;
}
};