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.
题意:
已知两个单词(起始单词和结束单词),一个单词词典,根据转换规则计算从起始单词到结束单词的所有最短转换路径。
转换规则如下:
1.转换时,只能转换单词中的1个字符。
2.转换得到的新单词,必须在单词词典中。
需要注意的是:
1.若无法转换到endWord,返回0。
2.所有单词长度相同。
3.只包含小写字符。
4.wordList中的无重复单词。
5.beginWord和endWord非空且不相同。
思考:
1.在宽度优先搜索时,如何保存搜索时的路径?
在宽度优先搜索中,将普通队列更换为vector实现,保存所有的搜索节点,即在pop节点时不会丢弃队头元素,只是移动front指针。在队列节点中,增加该节点的前驱节点的在队列中的下标,可通过该下标找到从哪个节点搜索到当前节点。
2.如果起始点与终点之间有多条路径,如何将多条路径全部搜索出?
到达某一位置可能有多条路径,使用映射记录到达每个位置的最短需要的步数,新扩展到的位置只要“未曾到达”或者“到达步数与最短步数相同”,即将该位置添加到队列中,从而存储了从不同前驱到达该位置的情况。
代码如下:
class Solution {
private:
struct Node
{
std::string mWord;
int mParentPos;
int mStep;
Node(std::string word, int parentPos, int step) :
mWord(word), mParentPos(parentPos), mStep(step) {}
};
bool IsLadder(const string& firstWord, const string& secondWord)
{
int count = 0;
for (int i = 0; i < firstWord.size(); i++)
{
if (firstWord[i] != secondWord[i])
{
count++;
}
}
return count == 1;
}
void ConstructGraph(const string& beginWord, vector<string>& wordList, map<string, vector<string>>& graph)
{
bool hasBeginWord = false;
for (int i = 0; i < wordList.size(); i++)
{
if (wordList[i] == beginWord)
{
hasBeginWord = true;
}
}
if (!hasBeginWord)
wordList.push_back(beginWord);
for (int i = 0; i < wordList.size(); i++)
{
for (int j = i + 1; j < wordList.size(); j++)
{
if (IsLadder(wordList[i], wordList[j]))
{
graph[wordList[i]].push_back(wordList[j]);
graph[wordList[j]].push_back(wordList[i]);
}
}
}
}
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
vector<Node> log;
map<string, int> visited;
map<string, vector<string>> graph;
vector<int> endWordPostion;
ConstructGraph(beginWord, wordList, graph);
visited[beginWord] = 1;
log.push_back(Node(beginWord, -1, 1));
int minCount = 0; //到达endWord的最小步数
int front = 0; //模拟队列头指针
while (front != log.size())
{
string word = log[front].mWord;
int count = log[front].mStep;
if (minCount != 0 && count > minCount) //所有到终点的路径都搜索完成
{
break;
}
if (word == endWord) //搜索到终点时,记录到达终点的最小步数
{
minCount = count;
endWordPostion.push_back(front);
}
for (int i = 0; i < graph[word].size(); i++)
{
if (visited.find(graph[word][i]) == visited.end() ||
visited[graph[word][i]] == count + 1)
{
log.push_back(Node(graph[word][i], front, count + 1));
visited[graph[word][i]] = count + 1;
}
}
front++;
}
//计算最终路径
vector<vector<string>> result;
for (int i = 0; i < endWordPostion.size(); i++)
{
int pos = endWordPostion[i];
vector<string> path;
while (pos != -1)
{
path.push_back(log[pos].mWord);
pos = log[pos].mParentPos;
}
result.push_back(vector<string>());
for (int j = path.size() - 1; j >= 0; j--)
{
result[i].push_back(path[j]);
}
}
return result;
}
};