Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence 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.
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
给定一个其实单词和一个终止单词,从起始单词出发,每次在所给单词列表中找一个单词有且只有一个字母与当前单词不同,如果能到达终止单词,返回最短路径的长度。
此题目与https://blog.csdn.net/kaotuduyazhuo4307/article/details/82257716中所述最少完全平方数思路形同。通过图的广度优先查找,得到最短路径,本文从直观思路出发,一步一步优化,最终给出一个相对优化的解。
class Solution {
public:
bool isTransformable(string s1, string s2){
int diffCount = 0;
for(int i=0; i < s1.length(); i ++){
if(s1[i] != s2[i])
diffCount ++;
}
return diffCount == 1;
}
//version 1.0
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//首先判断wordList是否包含endWord,否则返回0
vector<bool> visited(wordList.size(), false);
bool has = false;
for(string s : wordList){
if(s==endWord){
has = true;
break;
}
}
if(not has)
return 0;
queuestring , int> > queue1;
queue1.push(make_pair(beginWord, 1));
while(not queue1.empty()){
string curNode = queue1.front().first;
int step = queue1.front().second;
queue1.pop();
if(isTransformable(endWord, curNode)){
return step + 1;
}
for(int i=0; i < wordList.size(); i++){
if(not visited[i]){
if(isTransformable(wordList[i], curNode)){
queue1.push(make_pair(wordList[i], step + 1));
visited[i] = true;
}
}
}
}
return 0;
}
};
//优化查找
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//首先判断wordList是否包含endWord,否则返回0
unordered_set<string> toVisit(wordList.begin(), wordList.end());
if(toVisit.find(endWord) == toVisit.end())
return 0;
queuestring , int> > queue1;
queue1.push(make_pair(beginWord, 1));
while(not queue1.empty()){
string curNode = queue1.front().first;
int step = queue1.front().second;
queue1.pop();
if(isTransformable(endWord, curNode)){
return step + 1;
}
for (auto s = toVisit.begin(); s!=toVisit.end(); ){
if(isTransformable(*s, curNode)){
queue1.push(make_pair(*s, step + 1));
s = toVisit.erase(s);
} else
s++;
}
}
return 0;
}
注意:
(关于C++中set、unordered_set的使用) 如果使用range-based for
循环形式遍历set这种容器,使用.erase()
将前途未卜,如上所示,用auto s
去遍历toVisit这个无需集合,如果不加处理的使用toVisit.erase(s)将引发致命错误。
原因在于对s所指元素调用erase(),会使得s不在是toVisited的一个有效迭代器,此后未对s进行重新赋值就径直使用s会导致不明确行为
。解决方法如上所示,.erase()总是返回一个迭代起指向其后继的元素
,于是分别对待erase()和不需要erase()的两种情况即可。
//但是如果不需要返回查找路径,完全不需要存储太多中间变量,pair还是挺费时间和空间的;此外如果从两头向中间找,会节省很多时间
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//首先判断wordList是否包含endWord,否则返回0
unordered_set<string> wordSet(wordList.begin(), wordList.end());
unordered_set<string> beginNodes{beginWord};
unordered_set<string> endNodes{endWord};
unordered_set<string> *thisPointer = &beginNodes, *thatPointer = &endNodes;
unordered_set<string> nextNodes;
int step = 1;
if(wordSet.erase(endWord) == 0) // 如果list中不存在endWord,返回0;
return 0;
while(not thisPointer->empty() and not thatPointer->empty()){
if(thisPointer->size() > thatPointer->size())
swap(thisPointer, thatPointer);
for(auto curString=thisPointer->begin(); curString!=thisPointer->end(); curString++){
//遍历所有的当前节点
for(auto s=thatPointer->begin(); s!=thatPointer->end(); s++){
if(isTransformable(*curString, *s))
return step + 1;
}
for(auto s=wordSet.begin(); s!=wordSet.end();){
if(isTransformable(*curString, *s)){
nextNodes.insert(*s);
s=wordSet.erase(s);
}else
s ++;
}
}
*thisPointer = nextNodes;
nextNodes.clear();
step++;
}
return 0;
}
//进一步优化,更换对比方法
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//首先判断wordList是否包含endWord,否则返回0
unsigned long stringSize = beginWord.size();
unordered_set<string> wordSet(wordList.begin(), wordList.end());
unordered_set<string> beginNodes{beginWord}, endNodes{endWord}, nextNodes;
unordered_set<string> *thisPointer = &beginNodes, *thatPointer = &endNodes;
int step = 1;
if(wordSet.erase(endWord) == 0) // 如果list中不存在endWord,返回0;
return 0;
while(not thisPointer->empty()){
if(thisPointer->size() > thatPointer->size())
swap(thisPointer, thatPointer);
for (const auto &curString : *thisPointer) {
//遍历所有的当前节点
for(int i=0; i< stringSize; i++){
string nextNode = curString;
for(char c='a'; c<'z'; c++){
nextNode[i] = c;
if(thatPointer->find(nextNode)!=thatPointer->end())
return step + 1;
if(wordSet.find(nextNode) != wordSet.end()){
wordSet.erase(nextNode);
nextNodes.insert(nextNode);
}
}
}
}
*thisPointer = nextNodes;
nextNodes.clear();
step++;
}
return 0;
}
//去掉不必要的指针
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//首先判断wordList是否包含endWord,否则返回0
int stringSize = beginWord.size();
unordered_set<string> wordSet(wordList.begin(), wordList.end());
unordered_set<string> beginNodes{beginWord}, endNodes{endWord}, nextNodes;
int step = 1;
if(wordSet.erase(endWord) == 0) // 如果list中不存在endWord,返回0;
return 0;
while(not beginNodes.empty()){
if(beginNodes.size() > endNodes.size())
beginNodes.swap(endNodes);
for (const auto &curString : beginNodes) {
//遍历所有的当前节点
for(int i=0; i< stringSize; i++){
string nextNode = curString;
for(char c='a'; c<'z'; c++){
nextNode[i] = c;
if(endNodes.find(nextNode)!=endNodes.end())
return step + 1;
if(wordSet.find(nextNode) != wordSet.end()){
wordSet.erase(nextNode);
nextNodes.insert(nextNode);
}
}
}
}
beginNodes = nextNodes;
nextNodes.clear();
step++;
}
return 0;
}