版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/iov3Rain/article/details/88934320
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
- 每次转换只能改变一个字母。
- 转换过程中的中间单词必须是字典中的单词。
说明:
- 如果不存在这样的转换序列,返回 0。
- 所有单词具有相同的长度。
- 所有单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] 输出: 5 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
示例 2:
输入: beginWord = "hit" endWord = "cog" wordList = ["hot","dot","dog","lot","log"] 输出: 0 解释: endWord "cog" 不在字典中,所以无法进行转换。
用DFS和BFS都行,本题用BFS效率更高。
最开始想的是把每个单词能转成的单词用multimap记录下来。
然后再用BFS进行遍历,这样就减少了重复进行对比的次数。
但是如果单词多了起来后,效率就很低了。
class Solution
{
public:
int ladderLength(string beginWord, string endWord, vector<string> &wordList)
{
int n = beginWord.size();
multimap<string, string> mm;
for (int i = 0; i < wordList.size(); i++)
{
int cnt = 0;
for (int k = 0; k < n; k++)
{
if(beginWord[k] != wordList[i][k])
cnt++;
}
if(cnt == 1)
{
mm.insert(make_pair(beginWord, wordList[i]));
mm.insert(make_pair(wordList[i], beginWord));
}
}
for (int i = 0; i < wordList.size(); i++)
{
for (int j = i + 1; j < wordList.size(); j++)
{
int cnt = 0;
for (int k = 0; k < n; k++)
{
if (wordList[i][k] != wordList[j][k])
cnt++;
}
if (cnt == 1)
{
mm.insert(make_pair(wordList[i], wordList[j]));
mm.insert(make_pair(wordList[j], wordList[i]));
}
}
}
queue<string> q;
q.push(beginWord);
map<string, bool> visit;
visit[beginWord] = true;
int ans = 0;
while(!q.empty())
{
int size = q.size();
ans++;
for (int i = 0; i < size; i++)
{
string temp = q.front();
q.pop();
multimap<string, string>::iterator beg, end, it;
beg = mm.lower_bound(temp);
end = mm.upper_bound(temp);
for (it = beg; it != end; it++)
{
if((*it).second == endWord)
{
return ans + 1;
}
if (!visit[(*it).second])
{
visit[(*it).second] = true;
q.push((*it).second);
}
}
}
}
return 0;
}
};
优化1:倒着搜索比正着搜索效率更高,因为倒着搜索无效的结果比正着的少很多。
class Solution
{
public:
int ladderLength(string beginWord, string endWord, vector<string> &wordList)
{
int n = beginWord.size();
multimap<string, string> mm;
for (int i = 0; i < wordList.size(); i++)
{
int cnt = 0;
for (int k = 0; k < n; k++)
{
if(beginWord[k] != wordList[i][k])
cnt++;
}
if(cnt == 1)
{
mm.insert(make_pair(beginWord, wordList[i]));
mm.insert(make_pair(wordList[i], beginWord));
}
}
for (int i = 0; i < wordList.size(); i++)
{
for (int j = i + 1; j < wordList.size(); j++)
{
int cnt = 0;
for (int k = 0; k < n; k++)
{
if (wordList[i][k] != wordList[j][k])
cnt++;
}
if (cnt == 1)
{
mm.insert(make_pair(wordList[i], wordList[j]));
mm.insert(make_pair(wordList[j], wordList[i]));
}
}
}
queue<string> q;
q.push(endWord);
map<string, bool> visit;
visit[endWord] = true;
int ans = 0;
while(!q.empty())
{
int size = q.size();
ans++;
for (int i = 0; i < size; i++)
{
string temp = q.front();
q.pop();
multimap<string, string>::iterator beg, end, it;
beg = mm.lower_bound(temp);
end = mm.upper_bound(temp);
for (it = beg; it != end; it++)
{
if((*it).second == beginWord)
{
return ans + 1;
}
if (!visit[(*it).second])
{
visit[(*it).second] = true;
q.push((*it).second);
}
}
}
}
return 0;
}
};
优化2:
因为只有小写字母,字母只有26个,所以用在每个位置进行字母的变化,再用上set的高效率查找。
每次查找的复杂度为,字符串长度 * 26 * set.find()
倒着查找,效率更高。
class Solution
{
public:
int ladderLength(string beginWord, string endWord, vector<string> &wordList)
{
//查是否已经遍历过
unordered_map<string, bool> visit;
//增加查询速度
unordered_set<string> words(wordList.begin(), wordList.end());
if(words.find(endWord) == words.end())
return 0;
int ans = 0;
queue<string> q;
visit[endWord] = true;
q.push(endWord);
while(!q.empty())
{
int size = q.size();
++ans;
while(size--)
{
string front = q.front();
q.pop();
for (int i = 0; i < front.size(); i++)
{
string temp = front;
for (char j = 'a'; j <= 'z'; j++)
{
if(front[i] == j)
continue;
front[i] = j;
if(front == beginWord)
return ans + 1;
if(words.find(front) != words.end() && !visit[front])
{
visit[front] = true;
q.push(front);
}
}
front = temp;
}
}
}
return 0;
}
};