给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:
[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]
示例 2:
输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出: []
解释: endWord “cog” 不在字典中,所以不存在符合要求的转换序列。
寻找一个单词到另一个单词的最短转换序列(可能存在多个)。可以将每个单词看成图中的结点,两个单词转换规则是每次只能替换一个字母,相当于若两个单词只有一个字母不同,相当于两个结点有一条无向边相连。最后问题转化成求两个结点间的最短路径。
求最短路径用BFS或者dijkstra等算法均可,但这里需要求多条最短路径,用dijkstra算法更好。
注意:startWord与endWord可能都不在字典中,需要判断startWord是否在字典中,如果没有需要加入,作为路径的起点。
/*
将字典转为图,问题转为两点间的最短路径
*/
vector<string>words;
map<string, int>idxs;
map<string, vector<string>>pre;//存储前一个结点列表,因为可能存在多条最短路径
map<string, int>dist;
vector<string>tmp;
bool isSim(string a, string b){
int cnt = 0;
for(int i = 0; i < a.size(); ++i){
if(a[i] != b[i]){
++cnt;
}
}
return cnt == 1;
}
void dfs(vector<vector<string>>& res, vector<vector<int>>& pre, int u, int v){//递归生成多条路径
if(u == v){
res.push_back(tmp);
//tmp.pop_back();
return;
}
for(int i = 0; i < pre[u].size(); ++i){
tmp.push_back(words[pre[u][i]]);
dfs(res, pre, pre[u][i], v);
tmp.pop_back();//恢复环境
}
}
vector<vector<string>> dijkstra(vector<vector<int>>& gra, int u, int v){//dijkstra求最短路
int n = gra.size();
int MAX = 1 << 30;
int MIN = -(1 << 30);
int minVal = MAX, idx;
vector<bool>mark(n, false);
vector<int>dist(n, MAX);
vector<vector<int>>pre(n, vector<int>());
vector<vector<string>>res;
for(int i = 0; i < gra[u].size(); ++i){
pre[gra[u][i]] = vector<int>({u});
dist[gra[u][i]] = 1;
}
mark[u] = true;
for(int i = 0; i < n; ++i){
minVal = MAX;
idx = -1;
for(int j = 0; j < n; ++j){
if(!mark[j] && dist[j] < minVal){
minVal = dist[j];
idx = j;
}
}
if(idx != -1){
mark[idx] = true;
for(int k = 0; k < gra[idx].size(); ++k){
int v = gra[idx][k];
if(!mark[v] && dist[v] > dist[idx] + 1){
dist[v] = dist[idx] + 1;
pre[v].clear();
pre[v].push_back(idx);
}
else if(!mark[v] && dist[v] == dist[idx] + 1){
pre[v].push_back(idx);
}
}
}
}
//路径
if(pre[v].empty()){
return res;
}
else{
tmp.clear();//清空
tmp.push_back(words[v]);
dfs(res, pre, v, u);
for(int i = 0; i < res.size(); ++i){
reverse(res[i].begin(), res[i].end());
}
return res;
}
}
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
words.assign(wordList.begin(), wordList.end());
idxs.clear();//清空
pre.clear();
dist.clear();
bool flag = false;
for(int i = 0; i < wordList.size(); ++i){//建立字符串与下标的映射
idxs[wordList[i]] = i;
if(wordList[i] == beginWord){
flag = true;
}
}
if(!flag){
words.push_back(beginWord);
idxs[beginWord] = wordList.size();
}
int len = words.size();
vector<vector<int>>gra(len, vector<int>());//建立邻接表
for(int i = 0; i < len; ++i){//建立图
for(int j = i + 1; j < len; ++j){
if(isSim(words[i], words[j])){
gra[i].push_back(j);
gra[j].push_back(i);
}
}
}
if(idxs.find(endWord) == idxs.end()){
return vector<vector<string>>();
}
return dijkstra(gra, idxs[beginWord], idxs[endWord]);
}
};