这道题是给出起始的单词和最终的单词,再给出一个单词列表,问从起始的单词转换成最终的单词最少需要转换次数的步骤,可能有多个符合要求的答案,都要输出。如果两个单词只有一位的字母不同,则这两个单词可以转换。思路就是首先用map把单词都映射为int,然后把每个单词看作一个点,两个可以互相转换的单词连一条线,就构成了图,如果最终的单词不在图里可以直接输出。接下来就是难点了,因为这道题本质就是求最短路径,但是有可能有多条最短路径。我首先尝试了dfs回溯,结果超时,又进行剪枝,还是超时,又试了一些bfs改进打的方法,都是超时。看了题解,才知道用改进的bellman-ford可以求出,把数列中保存的点改成保存点的数组,这就相当于记录了路径,如果数组最后一个元素是最终的单词,即可把这个数组添加到答案,因为这个算法已经可以保证路径最短。
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
int sz=wordList.size(),len=beginWord.size();
vector<int> G[sz+5];
vector<vector<string> > ans;
unordered_map<string,int> mp;
unordered_map<int,string> cg;
int vis[sz+5],d[sz+5];
memset(vis,0,sizeof(0));
int cnt = 1;
mp[beginWord]=cnt++;
cg[cnt-1]=beginWord;
for(int i=0;i<sz;i++)
{
if(!mp[wordList[i]])
{
mp[wordList[i]]=cnt++;
cg[cnt-1]=wordList[i];
}
else continue;
int ok=0;
for(int j=0;j<len;j++)
if(beginWord[j]!=wordList[i][j])
{
ok++;
if(ok>1) break;
}
if(ok==1) G[1].push_back(mp[wordList[i]]);
}
for(int i=0;i<sz;i++)
if(wordList[i]!=beginWord)
for(int j=i+1;j<sz;j++)
if(wordList[j]!=beginWord)
{
int ok=0;
for(int k=0;k<len;k++)
if(wordList[i][k]!=wordList[j][k])
{
ok++;
if(ok>1) break;
}
if(ok==1)
{
G[mp[wordList[i]]].push_back(mp[wordList[j]]);
G[mp[wordList[j]]].push_back(mp[wordList[i]]);
}
}
if(!mp[endWord]) return ans;
memset(d,0x3f3f3f3f,sizeof(d));
vector<int> v,now;
v.push_back(1);
queue<vector<int> > q;
d[1]=0;
q.push(v);
int goal = mp[endWord];
while(!q.empty())
{
now=q.front();q.pop();
int b=now.back();
if(b==goal)
{
vector<string> vs;
for(int i=0;i<now.size();i++)
vs.push_back(cg[now[i]]);
ans.push_back(vs);
continue;
}
for(int i=0;i<G[b].size();i++)
{
int v=G[b][i];
if(d[v]>=d[b]+1)
{
d[v]=d[b]+1;
vector<int> tmp(now);
tmp.push_back(v);
q.push(tmp);
}
}
}
return ans;
}
};