滑动窗口是一种高效解决数组或字符串中子区间问题的技巧
我觉得较为经典的滑窗的背景问题是:
给你字符串
s
和整数k
。请返回字符串
s
中长度为k
的单个子字符串中可能包含的最大元音字母数。英文中的 元音字母 为(
a
,e
,i
,o
,u
)
在这个题目背景下,我们首先要明确“子字符串”(连续的字符集)和“子序列”(保持原有顺序关系的不要求连续的字符集)的区别
这类题目背景下,有两个变量(哪个子字符串)(对应的元音字母数)
我们记为x和y,那么这就是数学中的函数关系——“x是自变量,y是同时变的因变量”,在这样的情况下,时间复杂度就降为一次循环O(n)(原来的时间复杂度是 O(nk))
其实我们注意到,这个窗口是一个定长的,因此每次窗口移动“进入”和“退出”的字符数相同并且一头一尾——————那么我们循环时每次考虑(入-更新数-出)
class Solution {
public:
int maxVowels(string s, int k) {
int ans = 0, vowel = 0;
for (int i = 0; i < s.length(); i++) {
// 1. 进入窗口
if (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u') {
vowel++;
}
if (i < k - 1) { // 窗口大小不足 k
continue;
}
// 2. 更新答案
ans = max(ans, vowel);
// 3. 离开窗口
char out = s[i - k + 1];
if (out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u') {
vowel--;
}
}
return ans;
}
};
进阶——不定长滑窗
上面说了定长滑窗,理所当然的就有不定长的,其实这就是本系列上一篇“双指针”中的两个指针状态决定的,两个指针是同速还是不同速?是相向还是相对?
不定长滑窗的情况一般是异速同向类别的。
基本步骤就和上面的定长滑窗示例代码类似:
- 初始化:定义窗口的左右边界
left
和right
,通常从数组或字符串的起点开始。 - 扩展窗口:移动
right
(右边界),将新元素加入窗口。 - 收缩窗口:当窗口满足特定条件时,尝试移动
left
(左边界)以缩小窗口。 - 更新结果:在满足条件时,记录当前窗口的解,并根据需要进行更新。
- 重复步骤 2-4,直到右边界遍历完整个数组或字符串。
常见问题类型(没啥意义)
1. 最小窗口问题
描述:在一个字符串或数组中,找到满足特定条件的最小长度子区间。
例子:在字符串 s
中找到包含 t
中所有字符的最小窗口。
2. 最大窗口问题
描述:在数组中找到满足特定条件的最大长度子区间。
例子:在一个数组中,找到最多包含两个不同元素的最长连续子数组。
3. 子数组和问题
描述:找到数组中和大于等于目标值的最短子数组。
例子:在数组中找到和大于等于 target
的最短子数组。
4. 无重复子字符串问题
描述:找到字符串中无重复字符的最长子串。
推荐引流
这个就不给代码了,其实和上面的代码类似的,我还是实际点,本来就是“卷帙浩繁”的一个技巧了,到处都是解析的文章,我不献丑了,给出灵神的相关题单:
分享丨【题单】滑动窗口与双指针(定长/不定长/至多/至少/恰好/单序列/双序列/三指针) - 力扣(LeetCode)