滑动窗口(SildeWindow)
1 滑动窗口的思想
滑动窗口算法框架
伪代码
void slidingWindow(String s, String t) {
HashMap<Character, Integer> need = new HashMap<>();
HashMap<Characte,Integer> window = new HashMap<>();
for (char c : t.tCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int valid = 0;
while (right < s.length()) {
char c = s.charAt(right); // c 是将移入窗口的字符
right++; // 右移窗口
... // 进行窗口内数据的一系列更新
System.out.print("window: [%d, %d)\n", left, right); //debug 输出的位置
// 判断左侧窗口是否要收缩
while (window needs shrink) {
char d = s.charAt(left); // d 是将移出窗口的字符
left++; // 左移窗口
... // 进行窗口内数据的一系列更新
}
}
}
需要变化的地方
- 1、右指针右移之后窗口数据更新
- 2、判断窗口是否要收缩
- 3、左指针右移之后窗口数据更新
- 4、根据题意计算结果
在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 R 指针,和一个用于「收缩」窗口的 L 指针。
在任意时刻,只有一个指针运动,而另一个保持静止。
我们在 s 上滑动窗口,通过移动 r 指针不断扩张窗口。
当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
2 常见题型
2.1 (lee-567) 字符串的排列
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
输入: s1 = “ab” s2 = “eidbaooo”
输出: True
解释: s2 包含 s1 的排列之一 (“ba”)
方式 1: 滑动窗口
思路:由于排列不会改变字符串中每个字符的个数,所以只有当两个字符串每个字符的个数均相等时,一个字符串才是另一个字符串的排列。根据这一性质,记 s1 的长度为 n,我们可以遍历 s2中的每个长度为 n 的子串,判断子串和 s1 中每个字符的个数是否相等,若相等则说明该子串是 s1 的一个排列。
public boolean checkInclusion(String s1, String s2) {
// window 记录窗口中的字符, 记录需要凑齐的字符(need)
HashMap<Character, Integer> window = new HashMap<>();
HashMap<Character, Integer> need = new HashMap<>();
for (char c : s1.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int flag = 0;
while (right < s2.length()) {
char c = s2.charAt(right);
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (window.get(c).equals(need.get(c))) {
flag++;
}
}
while (right - left + 1 == s1.length()) {
if (flag == need.size()) {
return true;
}
char c1 = s2.charAt(left);
left++;
if (need.containsKey(c1)) {
if (window.get(c1).equals(need.get(c1))) {
flag--;
}
window.put(c1, window.get(c1) - 1);
}
}
right++;
}
return false;
}
方式 2:优化
/**
* 思路:由于需要遍历的子串长度均为 n,我们可以使用一个固定长度为 n 的滑动窗口来维护 cnt2
* 滑动窗口每向右滑动一次,就多统计一次进入窗口的字符,少统计一次离开窗口的字符。
* 然后,判断 cnt1 是否与