word ladder I 在看答案的基础上,搞懂了,主要是利用BFS,广度遍历,同时是几个辅助空间的构造,两个队列,承载每次在dict 里面找到的单词并作为下一次循环查找的单词。注意的是每次放进来的单词是必须没有访问过的,代码如下:
class Solution {
public:
int ladderLength(string start, string end, set<string> &dict){
queue<string> current,next;
unordered_map<string,string> record; // 记录dict里面的键是由哪个单词转换而来的。感觉在这里没有用到,但是如果要输出最短路径就用到了
unordered_set<string> visited;// 访问过的字符串存储下来
bool flag = false; // 找到了就终止,因为是广度,所以可以保证是最短
int level = 0;
current.push(start); // 从第一个开始,第一层上
while(!current.empty() && !flag)
{
level++;
while(!current.empty() && !flag) // 保证next层里面的数都穷
{
string curWord (current.front()); // 第一个单词出队,开始找下家
current.pop();
for(int i = 0;i < curWord.size();i++) // 按个字符进行替换
{
for(char c = 'a';c <= 'z';c++) // 这是题目中告诉你都是小写字母的原因,你可以从所有的小写字母中替换,是否可以优化,从dict里面取单词
{
string subWord (curWord);
if(c == subWord[i])
continue;
subWord[i] = c;
if(subWord == end)
{
flag = true;
record[subWord] = curWord;// 为了存储最短路径来的,本题目中可以不用
break;
}
if(dict.count(subWord) > 0 && visited.count(subWord) == 0)
{
next.push(subWord);// 下一层开始从这个队列开始,这里可能会放很多单词, hot ,cit hig
visited.insert(subWord);
record[subWord] = curWord; // 为了存储最短路径来的,本题目中可以不用
}
}
}
}
swap(next,current);
}
if(flag)
return level+1;
else
return 0;
}
};
其中record 变量是可以记录最短路径,并且将其输出的这样的辅助空间,本以为II就是让你输出,这样就可以直接应用了,但是II 来狠的,是让你输出所有最短路径,那么就不能单纯输出一条,这个时候遍历的时候就要做一些限制,想靠自己的力量在I的基础上进行更改:
更改点:
1)遇到有结尾之后,flag 不能在里层循环里面当成判断条件为true,否则不会有下一条路径。
2)visited 不能是全部的单词是否访问,这在一条路径中是可以的,但是多条路径的时候,visited要是上一层路径没有访问过的即可,当前层是可以出现重复单词的
例子:visited 变成了二维数组,这样占据了空间
Input: | "red", "tax", ["ted","tex","red","tax","tad","den","rex","pee"] |
Output: | [["red","ted","tad","tax"],["red","ted","tex","tax"]] |
Expected: | [["red","ted","tad","tax"],["red","ted","tex","tax"],["red","rex","tex","tax"]] |
class Solution {
public:
vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
vector<string> current,next;
multimap<string,string> record; // 记录dict里面的键是由哪个单词转换而来的。感觉在这里没有用到,但是如果要输出最短路径就用到了
vector<vector<string>> results;
vector<string>result;
vector<vector<string>> visited;// 要所有的,所以访问过的字符串要智能写,辨别每一层的
vector<string> dump;
dump.push_back(start);
visited.push_back(dump); // 第一层上无判重值
dump.clear();
bool flag = false; // 找到了就终止,因为是广度,所以可以保证是最短
int level = 0;
current.push_back(start); // 从第一个开始,第一层上
while(!current.empty() && !flag)
{
level++;
while(!current.empty()) // 保证next层里面的数都穷
{
string curWord (current.back()); // 第一个单词出队,开始找下家
current.pop_back();
for(int i = 0;i < curWord.size();i++) // 按个字符进行替换
{
for(char c = 'a';c <= 'z';c++) // 这是题目中告诉你都是小写字母的原因,你可以从所有的小写字母中替换,是否可以优化,从dict里面取单词
{
string subWord (curWord);
if(c == subWord[i])
continue;
subWord[i] = c;
if(subWord == end)
{
flag = true;
result.push_back(end);
getResult2(record,results,result,start,curWord);
result.clear();
continue;
// 这里就不break l,让其继续循环
}
if((dict.count(subWord) > 0 && ( level == 1||find(visited[level-2].begin(),visited[level-2].end(),subWord) == visited[level-2].end()))) // 在上一层里面找是否有访问过的,当前层不算
{
if(find(next.begin(),next.end(),subWord) == next.end())
next.push_back(subWord);//在作为下一层要进行处理的单词,不能出现重复的
if(visited.size() <= level )
visited.push_back(dump); // 防止溢出
visited[level].push_back(subWord);
record.insert(make_pair(subWord,curWord)); // 为了存储最短路径来的,本题目中可以不用
}
}
}
}
swap(next,current);
}
return results;
}
/*void getResult(unordered_map<string,string> record,vector<vector<string>> &results,string end,string start)
{
vector<string> result;
vector<string> finalR;
string keyStr = end;
result.push_back(end);
while(record[keyStr] != start)
{
result.push_back(record[keyStr]);
keyStr = record[keyStr];
}
result.push_back(start);
auto iter = result.rbegin();
for(;iter != result.rend();iter++)
finalR.push_back(*iter);
results.push_back(finalR);
}*/
// 这个递归是可以实现目的的!但是memeory 超时
void getResult2(multimap<string,string> record,vector<vector<string>> &results,vector<string> &result,string start,string keyStr)
{
result.push_back(keyStr);
if(keyStr == start)
{
reverse(result.begin(),result.end());
results.push_back(result);
return;
}
auto beg = record.lower_bound(keyStr);
auto end = record.upper_bound(keyStr);
while(beg != end)
{
getResult2(record,results,result,start,beg->second);
result.pop_back();
++beg;
}
}
};
但是上面的虽然符合条件,竟然memeory limited。。。。
继续优化,看了答案才知道,visited 可以不用设置二维数组,只要将其位置换一下即可,这个是想法的差距!!
class Solution {
public:
vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
vector<string> current,next;
multimap<string,string> record; // 记录dict里面的键是由哪个单词转换而来的。感觉在这里没有用到,但是如果要输出最短路径就用到了
vector<vector<string>> results;
vector<string>result;
vector<string> visited;// 要所有的,所以访问过的字符串要智能写,辨别每一层的 这里占内存了?
//vector<string> dump;
//dump.push_back(start);
//visited.push_back(dump); // 第一层上无判重值
//dump.clear();
bool flag = false; // 找到了就终止,因为是广度,所以可以保证是最短
int level = 0;
current.push_back(start); // 从第一个开始,第一层上
while(!current.empty() && !flag)
{
level++;
while(!current.empty()) // 保证next层里面的数都穷
{
for(auto word:current)
visited.push_back(word); // 将visited 放在最前面,就不用level去判断了!你要的只是上一层而已
string curWord (current.back()); // 第一个单词出队,开始找下家
current.pop_back();
for(int i = 0;i < curWord.size();i++) // 按个字符进行替换
{
for(char c = 'a';c <= 'z';c++) // 这是题目中告诉你都是小写字母的原因,你可以从所有的小写字母中替换,是否可以优化,从dict里面取单词
{
string subWord (curWord);
if(c == subWord[i])
continue;
subWord[i] = c;
if(subWord == end)
{
flag = true;
result.push_back(end);
getResult2(record,results,result,start,curWord);
result.clear();
continue;
// 这里就不break l,让其继续循环
}
if(find(visited.begin(),visited.end(),subWord)== visited.end() &&(dict.count(subWord) > 0) ) // 在上一层里面找是否有访问过的,当前层不算
{
if(find(next.begin(),next.end(),subWord) == next.end())
next.push_back(subWord);//在作为下一层要进行处理的单词,不能出现重复的
record.insert(make_pair(subWord,curWord)); // 为了存储最短路径来的,本题目中可以不用
}
}
}
}
swap(next,current);
}
return results;
}
// 这个递归是可以实现目的的!但是memeory 超时
void getResult2(multimap<string,string> record,vector<vector<string>> &results,vector<string> &result,string start,string keyStr)
{
result.push_back(keyStr);
if(keyStr == start)
{
reverse(result.begin(),result.end());
results.push_back(result);
return;
}
auto beg = record.lower_bound(keyStr);
auto end = record.upper_bound(keyStr);
while(beg != end)
{
getResult2(record,results,result,start,beg->second);
result.pop_back();
++beg;
}
}
};
但是这样更改后,仍然超时,没办法,继续优化,既然current 我目前仍然是当队列来处理的,遍历一个pop一个,可能费时,所以将其全部遍历之后一次性clear 再与next 交换,利用for(auto word:currenword) 结构更改如下:
class Solution {
public:
vector<vector<string>> findLadders(string start, string end, set<string> &dict) {
vector<string> current,next;
multimap<string,string> record; // 记录dict里面的键是由哪个单词转换而来的。感觉在这里没有用到,但是如果要输出最短路径就用到了
vector<vector<string>> results;
vector<string>result;
vector<string> visited;// 要所有的,所以访问过的字符串要智能写,辨别每一层的 这里占内存了?
//visited.push_back(dump); // 第一层上无判重值
bool flag = false; // 找到了就终止,因为是广度,所以可以保证是最短
current.push_back(start); // 从第一个开始,第一层上
while(!current.empty() && !flag)
{
for(auto word:current)
visited.push_back(word); // 将visited 放在最前面,就不用level去判断了!你要的只是上一层而已
for(auto curWord:current)
{
//string curWord (current.back()); // 第一个单词出队,开始找下家 不要每次都pop,
//current.pop_back();
for(int i = 0;i < curWord.size();i++) // 按个字符进行替换
{
for(char c = 'a';c <= 'z';c++) // 这是题目中告诉你都是小写字母的原因,你可以从所有的小写字母中替换,是否可以优化,从dict里面取单词
{
string subWord (curWord);
if(c == subWord[i]) continue;
subWord[i] = c;
if(subWord == end)
{
flag = true;
record.insert(make_pair(subWord,curWord));
continue;
// 这里就不break l,让其继续循环
}
if(find(visited.begin(),visited.end(),subWord)== visited.end() &&(dict.count(subWord) > 0) ) // 在上一层里面找是否有访问过的,当前层不算
{
if(find(next.begin(),next.end(),subWord) == next.end())
next.push_back(subWord);//在作为下一层要进行处理的单词,不能出现重复的
record.insert(make_pair(subWord,curWord)); // 为了存储最短路径来的,本题目中可以不用
}
}
}
}
current.clear();
swap(next,current);
}
if(flag)
getResult2(record,results,result,start,end);// 把建立路径的过程搞在外面来,不在里面处理
return results;
}
// 这个递归是可以实现目的的!但是memeory 超时
void getResult2(multimap<string,string> record,vector<vector<string>> &results,vector<string> &result,string start,string keyStr)
{
result.push_back(keyStr);
if(keyStr == start)
{
reverse(result.begin(),result.end());
results.push_back(result);
return;
}
auto beg = record.lower_bound(keyStr);
auto end = record.upper_bound(keyStr);
while(beg != end)
{
getResult2(record,results,result,start,beg->second);
result.pop_back();
++beg;
}
}
};
至此,已经跟答案相差无几了,但是。。仍然超时,那么就只能归结于是record 结构选择的问题了,我用multimap 实现的,实际上可以用map<string,vector<string>>来实现multimap ,那么是这个的区别?