leetcode的原题:
Given two words (beginWord and endWord), and a dictionary, find the length of shortest transformation sequence from beginWord to endWord, such that:
- Only one letter can be changed at a time
- Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
题意就是在一个单词的集合在中找出一个最短的序列,每次只改变一个字母,从初始单词得到目标单词。
首先感觉很明显要用到广搜。从初始单词开始,把每种变换的可能枚举出来压入队列,然后再把队列中的单词做同样的操作,找到目标单词则返回。这个地方可以用两个整形变量来记录循环的次数,变量lev1记录当前循环的单词数,lev2记录新的变换可能数。每当lev1为0时表明进入下一层变换,res++,lev1赋值为lev2。
int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) {
if (beginWord == endWord)
return 0;
int lev1 = 1, lev2 = 0, res = 0;
queue<string>que;
unordered_set<string>visited;
que.push(beginWord);
while (!que.empty())
{
string word = que.front();
que.pop();
--lev1;
for (unsigned int i = 0; i < word.size(); ++i)
{
for (auto j = 0; j < 26; ++j)
{
string newWord = word;
newWord[i] = 'a' + j;
if (newWord != word)
{
if (newWord == endWord)
return res + 2;
if (wordDict.find(newWord) != wordDict.end() && visited.find(newWord) == visited.end())
{
++lev2;
wordDict.erase(newWord);
que.push(newWord);
visited.insert(newWord);
}
}
}
}
if (!lev1)
{
++res;
lev1 = lev2;
lev2 = 0;
}
}
return 0;
}
这里有个地方注意一下,每次找到一个newWord后,可以在dict中把newWord erase掉,因为求的是最短序列,以后不会再用到这个单词。在leetcode oj上效率提高了几十个ms,不过还是花了467ms。
下面说一个优化了不少的办法,就是从两头同时开始进行查找。
用两个unordered_set来保留中间的生成的单词,然后再用他们生成新的单词集合,再进行查找。用这个办法leetcode上只花了81ms。
int ladderLength2(string beginWord, string endWord, unordered_set<string>& wordDict) {
unordered_set<string> beginSet, endSet, *set1, *set2;
int res = 1;
beginSet.insert(beginWord);
endSet.insert(endWord);
while (!beginSet.empty() && !endSet.empty())
{
if (beginSet.size() <= endSet.size())
{
set1 = &beginSet;
set2 = &endSet;
}
else
{
set1 = &endSet;
set2 = &beginSet;
}
++res;
unordered_set<string>tmp;
for (auto cur : *set1)
{
string tmpcur = cur;
for (auto &i : tmpcur)
{
char c = i;
for (auto j = 0; j < 26; ++j)
{
i = j + 'a';
if (set2->find(tmpcur) != set2->end())
return res;
if (wordDict.find(tmpcur) != wordDict.end())
{
tmp.insert(tmpcur);
wordDict.erase(tmpcur);
}
}
i = c;
//tmpcur = cur;
}
}
swap(tmp, *set1);
}
return 0;
}
这里遇到一个有意思的事情,我最开始还原tmpcur用的是将cur给tmpcur重新赋值,但是在oj上会出现wa,但是同样的例子在我自己电脑上调试是可以过的。看来oj上应该对这里进行了优化,tmpcur第二次赋值为cur时并没有重新读取cur的值,而是因为之前tmpcur = cur 所以直接就读了存tmpcur的寄存器,即将tmpcur赋值给了tmpcur。