滑动窗口类型总结

滑动窗口类型题目总结

1. 滑动窗口应用场景

  1. 子数组子串问题:
  2. 条件单调性场景,窗口扩大使得满足题目条件的概率增加,窗口减小必然使得题目条件概率减小。

第二点是关键,如果不满足,则无法使用滑动窗口方法来解决题目。

2. 基本框架

这里采用的框架是labuladong的滑动窗口框架,原因是简单好用,用的熟悉。

int left = 0; 
int right = 0; // 左右边界

while (right < s.size()) { // 滑动窗口的范围是[left, right),
    // 增大窗口
    window.add(s[right]);
    right++;

    while (window needs shrink) {
        // 缩小窗口
        window.remove(s[left]);
        left++;
    }

框架的使用需要考虑几个问题:

  1. 窗口什么时候增大,增大需要修改哪些状态
  2. 窗口什么时候减小,减小需要修改哪些状态
  3. 什么时候窗口满足条件,如何记录满足条件的结果

3. 例题应用

  1. 最小覆盖子串
    给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
    注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。
分析

首先,子串问题,可以考虑使用滑动窗口。第二,考虑题目的条件,是涵盖t的所有字符,可以想到,当窗口越大的时候,越有可能包含t中所有字符的子串;结合这两点,可以考虑用滑动窗口的方法解决问题。

在决定使用滑动窗口后,我们来思考框架使用的几个问题。

  1. 窗口在什么时候增大,增大需要修改哪些状态?
    答:当窗口没有覆盖t所有字符的时候,需要增大;增大的时候需要修改的变量有,窗口内的字符计数已经满足t的数量

  2. 窗口在什么时候需要减小,减小需要修改哪些状态?
    答:当窗口已经满足条件的时候,可以减小,增大的时候需要修改的变量有,窗口内的字符计数已经满足t的数量,直到窗口不满足条件为止。

  3. 什么时候窗口满足条件,如何记录满足条件的结果?
    答:当valid的数等于记录窗口。

class Solution {
    public String minWindow(String s, String t) {
    	// 建立窗口的记录和实际的记录
        Map<Character, Integer> window = new HashMap<>();
        Map<Character, Integer> need = new HashMap<>();
        // 初始化
        for(int i = 0; i < t.length(); i++){
            char cur = t.charAt(i);
            need.put(cur, need.getOrDefault(cur, 0) + 1);
        }
        int left = 0;
        int right = 0;
        int valid = 0;
        String result = "";
        int minLen = Integer.MAX_VALUE;
        // 左闭右开的区间
        while(right < s.length()){
            char cur = s.charAt(right);
            right++;
            // 增加窗口字母数量
            if(need.containsKey(cur)){
                window.put(cur, window.getOrDefault(cur, 0) + 1);
                if(window.get(cur).equals(need.get(cur))){
                    valid++;
                }
                // 优化, 移动左指针
                while(left < right && valid == need.size()){
                    char d = s.charAt(left);
                    if(need.containsKey(d)){
                        if(window.get(d).equals(need.get(d))){
                            valid--;
                            if(right - left < minLen){
                                result = s.substring(left, right);
                                minLen = right - left;
                            }
                        }
                        // 更新窗口
                        window.put(d, window.get(d) - 1);
                    }
                    left++;
                }
            }
        }
        return result;
    }
}

567. 字符串的排列

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        Map<Character,  Integer> need = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();

        for(int i = 0; i < s1.length(); i++){
            char cur = s1.charAt(i);
            need.put(cur, need.getOrDefault(cur, 0) + 1);
        }
        int left = 0;
        int right = 0;
        int valid = 0;
        int start = 0;
        int len = Integer.MAX_VALUE;
        while(right < s2.length()){
            char cur = s2.charAt(right);
            right++;
            if(need.containsKey(cur)){
                window.put(cur , window.getOrDefault(cur, 0) + 1);
                if(window.get(cur).equals(need.get(cur))){
                    valid++;
                }
            }
            while(right - left >= s1.length()){
                // 更新左节点
                if(valid == need.size()){
                    return true;
                }
                char d = s2.charAt(left);
                left++;
                if(need.containsKey(d)){
                    if(window.get(d).equals(need.get(d))){
                        valid--;
                    }  
                    window.put(d, window.getOrDefault(d, 0) - 1);
                }
            }
        }
        return false;
    }
}

3. 无重复字符的最长子串

分析
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> window = new HashMap<>();

        int left = 0;
        int right = 0;
        int maxLen = 0;
        while(right < s.length()){
            char cur = s.charAt(right);
            right++;
            if(!window.containsKey(cur)){
                window.put(cur, 1);
                maxLen = Math.max(right - left, maxLen);
            }
            else{
                while(left  + 1< right){
                    char d = s.charAt(left);
                    if(window.containsKey(cur)){
                        window.remove(d);
                    }
                    else{
                        break;
                    }
                    left++;
                }
                window.put(cur, 1);
            }
        }
        return maxLen;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值