双指针滑动窗口模板
1.使用场景:连续子串/子数组,求和,求序列长度
2.代码模板:
int left = 0;
int right = 0; //双指针,左右边界,表示区间[left, right]
int len = nums.length; //数组长度,字符串长度
int sum = 0; //统计子区间是否有效,可能是求和或者计数
int res = 0; //保存最大满足题目要求的 子数组/子串 长度
while(right < len) {
sum = sum + nums[right]; //增加右边界的值
while(区间[left, right] 不符合题目要求) {
sum = sum - nums[left];
left++; //移动左指针
}
sum = Math.max(res, right - left + 1); //更新结果
right++; //移动右指针
}
3.模板的思想:
滑动窗口用到了双指针,移动思路:
左指针不动,右指针向后遍历;
当不满足条件,右指针不动,剔除左指针的值,并向后移动左指针,直到满足条件。所以这里用while循环比较好。
然后对左右的边界更新的情况,进行结果的更新。
接着又开始移动右指针,right++,如果是用for循环,那个i就是右指针,i++就是移动右指针。
4.例题
76. 最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
- 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
- 如果 s 中存在这样的子串,我们保证它是唯一的答案。
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
粗线条思路:
问题要求我们返回字符串s中包含字符串t的全部字符的最小窗口。
属于找连续子串的覆盖,所以滑动窗口是完美适合;
所以,我们在s上滑动窗口,移动r指针不断扩张窗口,当窗口包含t全部字符,那么收缩窗口,更新得到最小的窗口。
具体分析:所以关键是如何判断窗口包含t中的所需的字符?
即比较窗口的字符,和t中的字符。
想到用两个哈希表,一个哈希表记录t中的所有字符及它们的个数;一个哈希表动态维护滑动窗口中所有的字符及它们的个数。
那么满足要求的条件就是:如果滑动窗口的哈希表,包含t的哈希表中的所有字符,并且对应的个数不小于t的哈希表的各个字符的个数,说明满足条件,记录当前窗口大小。
注意:这里 t 中可能出现重复的字符,所以我们要记录字符的个数。
//两个哈希表,记录字符和个数
Map<Character, Integer> ori = new HashMap<>();
Map<Character, Integer> ori = new HashMap<>();
public String minWindow(String s, String t) {
int tLen = t.length();
//获得t中的hash表,记录字符和个数
for(int i = 0; i < tLen; i++) {
char c = t.charAt(i);
ori.put(c, ori.getOrDefault(c, 0) + 1);
}
int l = 0, r = -1;//这里右边界是-1,所以循环的时候先r++
int len =Integer.MAX_VALUE, ansL = -1, ansR = -1;
int sLen = s.length();
while(r < sLen) { //遍历右边界指针
r++;
if(r < sLen && ori.containsKey(s,charAt(i))) {
cnt.put(s.charAt(i), cnt.getOrDefault(s.charAt(i), 0) + 1);
}//如果当前的s字符是t中的字符,放入哈希表中,记录个数
while(check() && l <= r) {
if(r - l + 1 < len) {
len = r - l + 1;
ansL = l;
ansR = l + len;//记录最小串得左右边界
}
if(ori.containsKey(s.charAt(l))) {
cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0) - 1 );
} //缩小窗口,移动左指针l,更新窗口的内容,即哈希表的内容
++l;
}
}
return ansL == -1 ? "" : s.substring(ansL, ansR);
}
//遍历哈希表,键值对转成entry,
public boolean check() {
Iterator iter = ori.entrySet().iterator();//哈希的迭代器
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();// 键值对转成entry
Character key = (Character) entry.getKey();
Integer val = (Integer) entry.getValue();
if(cnt.getOrDeault(key, 0) < val ) {//对应的字符的个数 小于说明不满足
return false;
}
}
return true;
}