1. 代码框架
- 使用哈希表+双指针实现;
- 双指针指用户维护滑动窗口的左右边界指针;
/* 滑动窗口算法框架 */
void slidingWindow(String s) {
// 用合适的数据结构记录窗口中的数据
HashMap<Character, Integer> window = new HashMap<>();
int left = 0, right = 0;
while (right < s.length()) {
// c 是将移入窗口的字符
char c = s.charAt(right);
window.put(c, window.getOrDefault(c, 0) + 1);
// 增大窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
// 注意在最终的解法代码中不要 print
// 因为 IO 操作很耗时,可能导致超时
System.out.printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (left < right && window needs shrink) {
// d 是将移出窗口的字符
char d = s.charAt(left);
window.put(d, window.get(d) - 1);
// 缩小窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
2. 力扣真题
2.1 最小覆盖子串
class Solution {
// 滑动窗口
public String minWindow(String s, String t) {
// 初始化哈希表
HashMap<Character,Integer> need=new HashMap<>();
for(char ch:t.toCharArray()){
need.put(ch,need.getOrDefault(ch,0)+1);
}
// 滑动窗口
HashMap<Character,Integer> window=new HashMap<>();
int n=s.length();
int left=0,right=0; // [left,right) 左闭右开区间
int start=0,len=Integer.MAX_VALUE; // 记录最小覆盖子串的信息
int valid=0;
while(right<n){
char c=s.charAt(right);
right++;
if(need.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(need.get(c).equals(window.get(c))){
valid++;
}
}
while(valid==need.size()){
if(right-left<len){
len=right-left;
start=left;
}
char d=s.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d))){
valid--;
}
window.put(d,window.get(d)-1);
}
}
}
return len==Integer.MAX_VALUE?"":s.substring(start,start+len);
}
}
2.2 字符串的排列
class Solution {
// 滑动窗口
public boolean checkInclusion(String s1, String s2) {
// 二者相等
if(s1.equals(s2)){
return true;
}
HashMap<Character,Integer> need=new HashMap<>();
for(char ch:s1.toCharArray()){
need.put(ch,need.getOrDefault(ch,0)+1);
}
// 窗口
HashMap<Character,Integer> window=new HashMap<>();
int n=s2.length();
int left=0,right=0;
int valid=0;
while(right<n){
// 扩大窗口
char c=s2.charAt(right);
right++;
if(need.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(need.get(c).equals(window.get(c))){
valid++;
}
}
while(valid==need.size()){
if(right-left==s1.length()){
return true;
}
char d=s2.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d))){
valid--;
}
window.put(d,window.get(d)-1);
}
}
}
return false;
}
}
2.3 找到字符串中所有字母异位词
class Solution {
// 滑动窗口
public List<Integer> findAnagrams(String s, String p) {
// 记录满足条件的下标
List<Integer> indexes=new ArrayList<>();
// 初始化哈希表
HashMap<Character,Integer> need=new HashMap<>();
for(char ch:p.toCharArray()){
need.put(ch,need.getOrDefault(ch,0)+1);
}
HashMap<Character,Integer> window=new HashMap<>();
int n=s.length();
int left=0,right=0;
int valid=0;
while(right<n){
// 扩大窗口
char c=s.charAt(right);
right++;
if(need.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(need.get(c).equals(window.get(c))){
valid++;
}
}
// 缩小窗口
while(valid==need.size()){
// 判断当前窗口内子串是否满足条件
if(right-left==p.length()){
indexes.add(left);
}
char d=s.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d))){
valid--;
}
window.put(d,window.get(d)-1);
}
}
}
return indexes;
}
}
2.4 无重复字符的最长子串
class Solution {
// 滑动窗口
public int lengthOfLongestSubstring(String s) {
// 窗口
HashMap<Character,Integer> window=new HashMap<>();
int n=s.length();
int left=0,right=0; // 窗口的左右边界
int res=0; // 记录最长子串的信息
while(right<n){
// 扩大窗口
char c=s.charAt(right);
right++;
window.put(c,window.getOrDefault(c,0)+1);
// 说明当前窗口有重复字符。缩小窗口
while(window.get(c)>1){
char d=s.charAt(left);
left++;
window.put(d,window.get(d)-1);
}
res=Math.max(res,right-left);
}
return res;
}
}
巨人的肩膀:
https://labuladong.gitee.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/wo-xie-le–f7a92/