今天这个题做了好几天呀,感觉不难理解,就是代码疯狂超时,最后决定还是先看看Word Ladder。
Word Ladder
此题的主要思路是利用BFS,我们把这一堆字符串看成一个个的节点,这里我们不需要真把这一个个节点构造成图,只需要记录他们就可以了。
思路:
比如我们第一层的字符串是beginWord,第二层字符串是所有beginWord能够到达的位置
第二层字符串构造好之后,第三层是所有第二层字符串能够到达的字符串
直到某一层我们看到了endWord,结束
层数即为所需要走的步数。
注意点:
1.字符串的移动具有可逆性,比如“hot”可以到“dot”,“dot”也可以到“hot”,所以我们需要利用hash记录字符串的访问情况,避免出现环路。
2.如何判断下一层的字符串?
这里有两种思路,一是遍历所有没有访问过的字符串,然后一个一个与当前字符串进行比较,事实证明--超时
二就是直接遍历字符串的所有字符,让字符串的每个字符进行26次替换得到新的字符串,然后再利用hash与没有访问过的字符串进行映射,看似很麻烦,实际上是可行的。
class Solution {
public:
vector<string> record;
unordered_set<string> pack;
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
for (int i = 0; i < wordList.size(); i++)
{
pack.insert(wordList[i]);
}
if (pack.find(endWord)==pack.end()) return 0;
record.push_back(beginWord);
int res = 0;
while (!record.empty())
{
res++;
vector<string> next;
for (int i = 0; i < record.size(); i++)
{
pack.erase(record[i]);
if (record[i] == endWord)
{
return res;
}
else
{
for (int j = 0; j < record[i].size(); j++)
{
string n = record[i];
for (int c = 'a'; c <= 'z'; c++)
{
n[j] = c;
if (pack.find(n)!=pack.end())
{
next.push_back(n);
pack.erase(n);
}
}
}
}
}
record = next;
}
return 0;
}
};
上面的代码的runtime是84ms,那么还有没有能优化的地方呢?
其实不难发现,如果wordlist相当巨大,那么每一层的大小是快速膨胀的,层越大,需要进行的字符替换操作就越多,时间也就越长。
如果我们把问题反过来,从endword作为一层,反推到beginword,问题的规模其实是一样的,但是如果我们从两头开始推,每次都选规模最小的层往下推,直到两头“对接”了,那么问题结束,时间上也会有很大程度上优化。
假设从beginword开始第一层推出的第二层有500个字符串,这个时候同样是走一层,从endword往前走这一层只有1个字符串,而从第二层则有500个。
优化代码如下:
class Solution {
public:
unordered_set<string> record;
unordered_set<string> record2;
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> pack(wordList.begin(), wordList.end());
if (pack.find(endWord)==pack.end()) return 0;
record.insert(beginWord);
record2.insert(endWord);
pack.erase(beginWord);
pack.erase(endWord);
int res = 1;
while (!record.empty())
{
res++;
unordered_set<string> next;
for (string s:record)
{
for (int j = 0; j < s.size(); j++)
{
string n = s;
for (int c = 'a'; c <= 'z'; c++)
{
n[j] = c;
if (record2.find(n) != record2.end())
{
return res;
}
if (pack.find(n)!=pack.end())
{
next.insert(n);
pack.erase(n);
}
}
}
}
record = next.size() > record2.size() ? record2 : next;
record2 = next.size() > record2.size() ? next : record2;
}
return 0;
}
};
优化过代码的runtime就达到了34ms
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
下面我们来看Word Ladder II
不光是层数了,要你具体求出路径
在Word Ladder的基础上,我的思路是这样的,BFS已经不能完全解决问题了,还得需要DFS,DFS的话我只对树或者图比较熟悉,所以这次就需要真的把图建立出来。
图节点数据结构如下:nb是下一层的节点集
struct node
{
string val;
vector<node*> nb;
node(string s) :val(s) {};
};
图的构造是主要麻烦的地方,dfs很简单,bfs就利用上一题的思路就行
图的构造也用到了hash
注意点:
1.可能有同一层的两个不同节点同时指向下一层同一节点
2.可能存在同一层某一节点可以指向本层另一节点,这样是不被允许的,因为白白增加路径长度
3.所有的节点都应提前构造好,访问的时候直接拿来用,如果new一个新节点容易出现问题
class Solution {
public:
struct node
{
string val;
vector<node*> nb;
node(string s) :val(s) {};
};
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> record(wordList.begin(), wordList.end());
unordered_map<string, node*> noderec;
for (int i = 0; i < wordList.size(); i++)
{
noderec.insert(std::make_pair(wordList[i], new node(wordList[i])));
}
unordered_set<node*> next;
vector<vector<string>> res;
noderec[beginWord] = new node(beginWord);
node* ld = noderec[beginWord];
if (record.find(endWord) == record.end()) return res;
record.erase(beginWord);
//record.erase(endWord);
vector<string> rec;
rec.push_back(beginWord);
next.insert(ld);
while (!next.empty())
{
unordered_set<node*> tmp;
for (node* s : next)
{
record.erase(s->val);
if (s->val == endWord)
{
tmp.clear();
break;
}
for (int i = 0; i < s->val.size(); i++)
{
string n = s->val;
for (int j = 'a'; j <= 'z'; j++)
{
n[i] = j;
if (record.find(n) != record.end()&&next.find(noderec[n])==next.end())
{
s->nb.push_back(noderec[n]);
if(tmp.find(noderec[n])==tmp.end())
tmp.insert(noderec[n]);
}
}
}
}
next = tmp;
}
vector<string> local;
local.push_back(beginWord);
dfs(res, noderec[beginWord], endWord, local);
return res;
}
void dfs(vector<vector<string>> &res, node *root,string &es,vector<string> &local)
{
if (!root) return;
if (root->val == es)
{
res.push_back(local);
return;
}
for (int i = 0; i < root->nb.size(); i++)
{
local.push_back(root->nb[i]->val);
dfs(res, root->nb[i], es, local);
local.pop_back();
}
}
};