滑动窗口问题 | 豆包MarsCode AI刷题

63 阅读5分钟

开篇吐槽

谈到滑动窗口算法,我个人的感觉是想法很美好,现实很残酷,每次写滑动窗口的题,都是思路满满,代码慢慢,窗口的维护,真让人头大,那么多测试用例,就你不通过,头疼。。。

v2-073e247e5d602624d54eb3cd9294f63f_r.jpg

看了很多人的博客,发现了一套模板,希望你面对此类问题可以砍瓜切菜,如入无人之境,下面进入正题....

算法逻辑

滑动窗口算法本质就是维护窗口,不断更新。容易出错的地方就是窗口的维护,满足什么条件扩大窗口、什么条件缩小窗口,这虽然因题而异,但希望下面的框架能为你起到帮助....

public String slidingWindow(String s, String t) {
        int size = 0;    // 实际窗口大小
        int need = 0;   // 需要窗口大小1)计算需要的窗口大小
        for(int i = 0; i < t.length(); i++) {
            if(条件判断) need++;
        }
        
        for(int i = 0, j = 0; i < s.length(); i++) {
            (2)实际窗口扩展
            if(判断条件) size++;
            
            (3)当实际窗口大小满足需要窗口大小时,开始收缩窗口
            while(判断条件) {
                
                计算答案
            
                (4)实际窗口缩小
                if(判断条件) size--;
                j++;
            }
        }
        return "";
    }

这里指的窗口大小是根据条件计算得到的,并非i,j之间的距离

  1. 计算需要窗口大小【这一步很关键,它决定后面我们扩展和缩小窗口的具体判断条件,一定要想清楚】
  2. 实际窗口扩展
  3. 当实际窗口大小满足需要窗口大小时,开始收缩窗口【注意边界,防止溢出;注意最终答案的更新条件,避免漏判】
  4. 实际窗口缩小

题目自查

  1. 最小覆盖子串 - 力扣(LeetCode)

题目预览
给定两个字符串 s 和 t 。 返回 s 中包含 t 的所有字符的最短子字符串。
如果 s 中不存在符合条件的子字符串,则返回空字符串 "" 。
如果 s 中存在多个符合条件的子字符串,返回任意一个。
注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

  • 计算需要窗口大小:题目指出s中包含t的全部字符,那么我们计算t中字符的种类作为需要窗口的大小
  • 实际窗口扩展:当s中的字符在t中并且所需的数量刚好等于最小数量时,这时扩展实际窗口的大小。为什么是刚好等于,而不是大于等于,因此大于等于会导致重复窗口重复扩展。
  • 当实际窗口大小满足需要窗口大小时,开始收缩窗口
  • 实际窗口缩小:当s中的字符在t中,减少对应的数量,如果数量小于需要的,缩小窗口
public String minWindow(String s, String t) {
        char[] ch = s.toCharArray();
        char[] ct = t.toCharArray();
        int left = 0;
        int right = ch.length+1;
        int size = 0;
        int need = 0;
        int[][] pos = new int[2][128];
        for(char c : ct) {
            pos[0][c - 'A']++;
        }
        for(int i = 0; i < 128; i++) {
            if(pos[0][i] > 0) need++;
        }
        for(int i = 0, j = 0; i < ch.length; i++) {
            for(char c : ct) {
                if(c == ch[i]) {
                    pos[1][ch[i] - 'A']++;
                    if(pos[1][ch[i] - 'A'] == pos[0][ch[i] - 'A']) {
                        size++;
                    }
                    break;
                }
            }
            while(size == need && j <= i) {
                if(right - left > i - j) {
                    right = i;
                    left = j;
                }
                for(char c : ct) {
                    if(c == ch[j]) {
                        pos[1][ch[j] - 'A']--;
                        if(pos[1][ch[j] - 'A'] < pos[0][ch[j] - 'A']) {
                            size--;
                        }
                        break;
                    }
                }
                j++;
            }
        }
        return right - left == ch.length+1 ? "" : s.substring(left, right+1);
    }

2、最小替换子串长度 - MarsCode

题目描述:
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。

看了上面的题目,大家有没有发现这两道题很相似,只是换了种说法。
题目二要求我们找到最小替换条件的组最短字串,可是题目中表明经过替换每个字符出现的字数是相等的,因此最小的替换的次数是固定不变的,我们只需要将多的字符换成少的字符即可,即找到出现多的字符在字符串中的最小长度。 综上所述,题目二本质是再求最小覆盖字串,文字不好理解,看示例:

String s = "AAASFDFF"
对于字符串s来说,最小的替换次数一定是2(替换一个A和F),那么寻找最小替换字串长度就是找字符串s中字符串t="AF"的最小覆盖字串,因此题目完美解决。
代码如下:

public static int solution(String input) {
        char[] str = input.toCharArray();
        int size = str.length/4;
        int ans = Integer.MAX_VALUE;
        int[] dp = new int[26];
        int need = 0;
        int window = 0;
        int[] count = new int[26];
        for (char c : str) {
            dp[c - 'A']++;
        }
        for (int d : dp) {
            if(d > size) need++;
        }
        if(need == 0) return 0;
        for (int i = 0, j = 0; i < str.length; i++) {
            count[str[i] - 'A']++;
            if(dp[str[i] - 'A'] > size && count[str[i] - 'A'] == dp[str[i] - 'A'] - size) {
                window++;
            }
            while(window == need && j <= i) {
                ans = Math.min(ans, i-j+1);
                count[str[j] - 'A']--;
                if(dp[str[j] - 'A'] > size && count[str[j] - 'A'] < dp[str[j] - 'A'] - size) {
                    window--;
                }
                j++;
            }
        }
        return ans;
    }

总结

  1. 面对此类问题一定要认真分析题目,确实计算窗口大小的条件
  2. 在计算最终结果时,一定要确保能找到所有的情况,避免漏判
  3. 题目中给解题代码不是最佳,大家请自行优化

f370a7ae68de9263f82f6e8fc76b8ef4.jpeg