滑动窗口技巧解决子串问题

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/

猜你喜欢

转载自blog.csdn.net/qq_43665602/article/details/131543954