题目:
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, 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"]
Return
[ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
Note:
- All words have the same length.
- All words contain only lowercase alphabetic characters.
思路:
陷阱比较多的一道题目。最基本的思路是dijkstra算法。但是空间与时间复杂度较高。更好的思路是BFS,但是要注意几个问题:
1)上一层可能有多个结点指向下一层,因此可以产生多个路径。如下图B->E,C->E。
2)尽管上一层有多个结点指向本层一个结点,但是在BFS的时候该结点只需要遍历一次。访问B或者C时,程序都会尝试将E放入BFS的queue。
3)对于空间的优化:
1- dict可能很大,建议直接使用而不是复制到class中
2- 输出结果的保存,直接用vector存储这个结点链需要大量的空间开销。我采用的方法是一个字符串对pair<string, string>。第一个字符串代表子节点的字符串,第二个字符串代表父节点的字符串。因此从end出发递归可以求出整个结点链。
3- queue要保存需要访问的结点,并且还需要存储结点的层级。我采用的方式是两个queue,一个保存上一层级的已经访问的结点,另一个保存下一层级需要访问的结点。
4- 需要判断某个结点是否已经出现在queue中,因此queue采用set保存比较合适。
5- 遍历一个层次的结点后将这个层次的结点删除,防止字符串变形反向进行。
代码:
class Solution {
public:
vector<string> path;
unordered_multimap<string, string> childToParent;
vector<vector<string>> results;
int level;
vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
set<string> nodes[2];
nodes[1].insert(start);
pair<string,string> ctp (start,"");
childToParent.insert(ctp);
removeItem(end,dict);
level=0;
while(!nodes[0].empty()||!nodes[1].empty())
{
if(nodes[level%2].empty())
{
level+=1;
for(set<string>::iterator p=nodes[level%2].begin();p!=nodes[level%2].end();p++)
{
removeItem(*p,dict);
}
}
set<string>::iterator ptr = nodes[level%2].begin();
string cur = *ptr;
nodes[level%2].erase(ptr);
if(cur==end)
{
break;
}
string next = cur;
next = changeStr(cur, next, end, dict);
while(next!="")
{
pair<string,string> ctp (next,cur);
childToParent.insert(ctp);
set<string>::iterator ptr = nodes[(level+1)%2].find(next);
if(ptr==nodes[(level+1)%2].end())
nodes[(level+1)%2].insert(next);
next = changeStr(cur, next, end, dict);
};
};
getPath(end, start, 1);
return results;
}
string changeStr(string current, string begin, string target, unordered_set<string> &dict)
{
int s=0;
int l=0;
if(begin!=current)
{
for(;s < current.size();s++)
{
if(current[s] != begin[s])
{
break;
}
}
l = begin[s]-'a'+1;
}
if(l==26)
{
s++;
l=0;
}
for(int i=s;i<current.size();i++)
{
for(int j=(i==s)?l:0;j<26;j++)
{
if(current[i]!='a'+j)
{
string tmp = current;
tmp[i] = 'a'+j;
if(tmp==target)
{
return tmp;
}
else
{
if(findItem(tmp,dict))
{
return tmp;
}
}
}
}
}
return "";
}
void getPath(string str, string start, int pathLen)
{
if(str == start)
{
if(pathLen==level)
{
vector<string> r;
r.push_back(str);
for(int i=path.size()-1;i>=0;i--)
{
r.push_back(path[i]);
}
results.push_back(r);
}
}
else
{
pair<unordered_multimap<string, string>::iterator,unordered_multimap<string,string>::iterator > range = childToParent.equal_range(str);
for(unordered_multimap<string, string>::iterator ptr=range.first;ptr!=range.second;ptr++)
{
path.push_back(ptr->first);
getPath(ptr->second, start, pathLen+1);
path.pop_back();
}
}
}
bool haveItem(string str)
{
unordered_multimap<string,string>::const_iterator got = childToParent.find(str);
if ( got == childToParent.end() )
return false;
else
return true;
}
string getParent(string str)
{
unordered_multimap<string,string>::const_iterator got = childToParent.find(str);
if ( got == childToParent.end() )
return NULL;
else
return got->second;
}
void removeItem(string s, unordered_set<string> &dict)
{
unordered_set<string>::const_iterator p = dict.find(s);
while(p!=dict.end())
{
dict.erase(p);
p = dict.find(s);
};
}
bool findItem(string s, unordered_set<string> &dict)
{
unordered_set<string>::const_iterator p = dict.find(s);
if(p!=dict.end())
{
return true;
}
else
{
return false;
}
}
};