(以前不该觉得力扣怎么大都这么简单,真是应该好好看看官方题解,思路好妙啊,我错了,学到了)
题目:
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> … -> sk:
每一对相邻的单词只差一个字母。
对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
sk == endWord
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。
题解:
先不考虑优化,只考虑把这题给过了
先把 beginWord 放入 wordList 方便处理
再先看一眼数据,1 <= wordList.length <= 5000,好,算法复杂度应该控制在O(n^2),那直接话不多说,打表,如果两个字符串之间只有一个字符不同,则为 true,否则为 false
那么我们要求最短序列,这不就最短路,然后边的权值都是1,bfs 过一遍,完
代码如下:
class Solution {
public:
bool flag[5005][5005];
bool fff[5005];
int n;
queue<pair<int, int> > temp;
int solve(int a, int p) {
fff[a] = true;
temp.push(make_pair(a, 1));
while(!temp.empty()) {
pair<int, int> t = temp.front();
temp.pop();
for(int i = 0; i < n; i++) {
if(i == p && flag[t.first][i]) return t.second + 1;
if(!fff[i] && flag[t.first][i]) {
fff[i] = true;
temp.push(make_pair(i, t.second + 1));
}
}
}
return 0;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
wordList.insert(wordList.begin(), beginWord);
int f = -1;
n = wordList.size();
for(int i = 0; i < wordList.size(); i++) {
if(wordList[i] == endWord) f = i;
flag[i][i] = false;
for(int j = i + 1; j < wordList.size(); j++) {
int t = 0;
for(int k = 0; k < beginWord.size(); k++) {
if(wordList[i][k] != wordList[j][k]) t++;
}
if(t == 1) flag[i][j] = flag[j][i] = true;
else flag[i][j] = flag[j][i] = false;
}
}
cout << n << endl;
if(f == -1) return 0;
return solve(0, f);
}
};
然后去看了眼官方题解,感觉确实很妙,两处优化:
① 优化建图
(字符串数量给的就不应该这么少,不然像我这样菜的肯定会用两两比较过,都不会考虑其他方法)
我们这样每两个数据比较是 O(n^2),但是这样我们会不会效率太低呢
考虑,能不能用空间换效率,怎么个换法呢
比如 “abcde”,我们是不是可以保存 “bcde","acde”,“abde"…,那么我们这样保存的话,是不是所有 "acde” 对应的字符串都是改变一个字符就能相同的
最后我们把每个 “a*cde” 这样对应的字符串两两做一条边
考虑一下 “a*cde” 最多也就 26 个对应的字符串,因为 * 对应不同的字母,所以不会超时
但是有没有更优的做法呢,毕竟两两操作,多少有些浪费
当然可以,如果我们把 “acde" 对应的字符串都和 "acde” 连一条边,那么也不就相当于每个字符串之间都有一条通路,只不过路径长度从原本的 1 变成 2,最后把结果除 2 即可,这样子显然更优
(注意,我们怎么保存 “a*cde” 对应的字符串呢,这肯定想到的是 map,键为 string,值为 vector,但是我们用 unordered_map 更优,用不排序的哈希表,速度更快)
② 优化 bfs
(借用官方图,一眼懂)
分别从起点和终点进行广搜,当碰到同一个点的时候,结束搜索
(怎么确定碰到了同一个点呢,用一个 unordered_map 来存当前遍历到的点,所经过的路径长度,如果 unordered_map 中已经有了该键,则说明已经遍历过,所以可以判断碰到同一个点,此时结束搜索)