引言
滑动窗口实际上也属于双指针的技巧范畴,利用两个指针圈定了一个范围。思想简单,但是细节满满,比如什么时候需要扩大窗口(right++),什么时候需要缩小窗口(left++),什么时候更新数据。主要就是这几点,把这几点弄清楚了,那么滑动窗口的问题也就解决了。
适合解决的问题
滑动窗口适合于解决连续的子数组,子串等问题,并不适合于解决非连续性的问题。
基本的框架
1 | int left = 0, right = 0; |
无重复字符的最长子串
这是leetcode的一道题目。我们下面分析下
- 首先我们需要一个hash表来存放窗口中的数据,因为是无重复子串,所以每个字符出现一次,可以用unordered_map
- 当right向右滑动扩大窗口时,给窗口的unordered_map增加数据
- 然后判断窗口是否应该收缩了,如果是,则将left向滑动,然后更新unordered_map减少数据,
- 最后更新结果数据
具体的代码逻辑如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = 0;
int right = 0;
int ret = 0;
unordered_map<char, int> windows;
while (right < s.length()) {
// 扩大窗口
char c = s[right];
windows[c]++;
right++;
// 检查是否满足收缩条件,然后开始收缩
while (windows[c] > 1) {
char d = s[left];
windows[d]--;
left++;
}
// 收缩完毕后,开始更新数据
ret = max(ret, right - left);
}
return ret;
}
};
我们来看细节,扩大窗口的时候,直接将c更新到窗口中,然后直接判断是否需要收缩,而收缩也是直接对c进行while循环,如果条件成立,那就是因为新增加了c导致窗口需要收缩(否则在之前添加元素的时候就收缩了),所以才会是while (windows[c] > 1),在循环内部不停的收缩窗口,知道满足条件。这是说明窗口是满足条件的,需要计算ret的最大值。
最小覆盖子串
对于这个题目,处理的是连续的子串,因此用滑动窗口是非常合适的。那么自然的按照滑动窗口的套路:
- right不停的向右扩张,直到窗口的子串符合条件,right停止扩张
- 然后left向右扩张,直到窗口中的子串不再符合条件,left停止扩张
- 然后重复上面的过程,直到right到达终点
有两个需要注意的点
- 怎样判定窗口中的子串复合条件,我们可以用一个unordered_map存放窗口中的数据,每次为窗口新增数据时,判断是否满足right停止扩张条件
- 需要新增一个标志valid来决定right和left的扩张与停止
1 | class Solution { |
找到字符串中所有字母异位词
这个题目用上面的思路也是可以解决的,主要有一点需要注意
- 这个题目是一个固定大小的窗口,这会相对简单一些
1 | class Solution { |
字符串的排列
这道题目跟上面的找到字符串中所有字母异位词基本是一个意思,实际上这还是一个弱化版本的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37class Solution {
public:
bool checkInclusion(string s1, string s2) {
unordered_map<char, int> needs;
for (auto item : s1) {
needs[item]++;
}
int left = 0;
int right = 0;
int valid = 0;
unordered_map<char,int> windows;
while (right < s2.length()) {
char c = s2[right++];
if (needs.count(c)) {
windows[c]++;
if (windows[c] == needs[c]) {
valid++;
}
}
if (right - left == s1.length()) {
if (valid == needs.size()) {
return true;
}
char d = s2[left++];
if (needs.count(d)) {
if (needs[d] == windows[d]) {
valid--;
}
windows[d]--;
}
}
}
return false;
}
};
长度最小的子数组
前面的例题主要是字符串的内容,下面我们看一个数组的问题。这个题目还是比较简单的。
1 | class Solution { |