零、本文只是自己做题时候的想法,仅供参考
一、滑动窗口,把长字符串上嵌入一个窗口,并滑动,好处有二:
1.窗口内的数据较少便于维护
2.滑动过程通常只需要遍历一遍主串
二、滑动窗口的“三步走”:
1.进窗口
2.判断出窗口条件
3.更新窗口
三、题目及代码详解:
补充:如果不习惯这里“先预测再执行”的更新窗口逻辑,其实也可以“先执行再更新”,不过那样就不是维护滑动窗口而是维护滑动窗户框了吧。

#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
// 滑动窗口
vector<int> ans;
int s_len = s.length();
int p_len = p.length();
int count[26];
if (p_len > s_len) {
return ans;
}
// 更新count,记录初始窗口主串和模式串的差异
for (int i = 0; i < p_len; i++) {
++count[s[i] - 'a'];
--count[p[i] - 'a'];
}
// 用differ记录不同字符的数量
int differ = 0;
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
++differ;
}
}
// 初始位置判断
if (differ == 0) {
ans.push_back(0);
}
// 滑动窗口
for (int i = 0; i < s_len - p_len; ++i) {
// 1.重点是滑动时的变化,只要关注变化前后s和p是否相同字符数变多(变少)就可以了,
// 也就是说,只要看是不是+-1->0(异变同),或者0->+-1(同变异),除此之外的情况都会在窗口滑动的过程中自动维护,
// 花开两朵,各表一枝:如果便会-+1或0,按照第一条处理;其他情况,因为维护的是count数组(窗口)和differ,一定是s和p不同;
// count数组目前记录的是未滑动之前初始状态时s和p的differ,
// 2.每次滑动更新一个窗口右侧字符和窗口左侧字符,左出(减)右进(加),
// 出窗口判断(维护窗口左端)
if (count[s[i] - 'a'] == 1) {
// 初始窗口是我们前面自行构建的,这里的判断条件要和4.出窗口相结合去看,
// 这个语句的count索引“[s[i] - 'a']”和维护窗口的索引一致,也就是先预测执行结果,然后执行,
// (这两个if/else if判断其实是一回事,只不过一个在左一个在右罢了)
// 事实上,++differ的时刻,count数组还没有变化。
// 总而言之重点还是“变化”;。
--differ;
}
else if (count[s[i] - 'a'] == 0) {// 一个“相同”因为维护移动窗口二减少了
++differ;
}
// 4.出窗口(维护窗口左端)
--count[s[i] - 'a'];
// 进窗口判断
if (count[s[i + p_len] - 'a'] == -1) {
// 举个例子,假设这是我们程序运行的某一时刻,我们的程序停止了(其实就是调试)
// 这个“(count[s[i + p_len] - 'a'] == -1)”, 等于-1一定会被下面的5.进窗口(维护窗口右端)+1,变成0的,也就是异到同
--differ;
}
else if (count[s[i + p_len] - 'a'] == 0) {// 同理由同到异
++differ;
}
// 5.进窗口(维护窗口右端)
++count[s[i + p_len] - 'a'];
// differ等于0意味着这一时刻s和p的差异为0(异位词),记录索引
// 至于+1,这时候虽然完成了维护,但是++i的步长还没有执行,所以+1才能正确定位
if (differ == 0) ans.push_back(i + 1);
}
return ans;// 最后不要忘记返回结果
}
};
int main() {
return 0;
}
四、结尾:
本质上滑动窗口和双指针是普遍性和特殊性的关系,故而应当考虑双指针的一些条件:比如双指针的本质:优化和双指针的使用前提:单调性。
最后,如有问题,欢迎讨论和指正。
274

被折叠的 条评论
为什么被折叠?



