滑动窗口问题

本文探讨了滑动窗口在解决字符串处理问题中的优化方法,如找到无重复字符的最长子串,以及最小覆盖子串。通过使用滑动窗口和字符频率数组,可以有效地减少时间复杂度,提高算法效率。文章提供了具体的Java代码实现,解释了滑动窗口在处理字符串匹配和子串查找问题时的关键策略和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以数组为载体完成的任务中,有一类问题以「滑动窗口」为标签。

例题:「力扣」第 3 题:无重复字符的最长子串

力扣相关题型 第76题,第209题,第424题,第438题,第567题。

方法一:暴力解法
一个最直接的思考方案是:

枚举这个字符串的所有子串;
对于每一个子串都判断一下这个子串是否有重复字符;
在从没有重复字符的所有子串中

public class Solution {

    public int lengthOfLongestSubstring(String s) {
        int len = s.length();

        int maxLen = 1;
        // 枚举到 len - 2 即可
        for (int left = 0; left < len - 1; left++) {
            for (int right = 0; right < len; right++) {
                String subString = s.substring(left, right + 1);
                // 如果 subString 不包含重复元素,记录 subString 的长度,并且维护 maxLen
            }
        }
        return maxLen;
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/leetbook/read/learning-algorithms-with-leetcode/x12j0r/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

滑动窗口的优化。

public class Solution {

    public int lengthOfLongestSubstring(String s) {
        // 重复元素上一次出现的位置很重要
        int len = s.length();
        if (len < 2) {
            return len;
        }

        // 当做哈希表使用。key:字符的 ASCII 值,value:最新下标,-1 表示当前字符在遍历的时候还未出现过
        int[] window = new int[128];
        for (int i = 0; i < 128; i++) {
            window[i] = -1;
        }

        char[] charArray = s.toCharArray();

        int res = 1;
        int left = 0;
        // [left, right) 没有重复元素
        for (int right = 0; right < len; right++) {
            if (window[charArray[right]] != -1) {
                left = Math.max(left, window[charArray[right]] + 1);
            }
            window[charArray[right]] = right;
            // 注意理解这里为什么是 + 1
            res = Math.max(res, right - left + 1);
        }
        return res;
    }
}

力扣   第76题,最小覆盖子串

做法思路     注意点:

1.     如何判断字符串中出现的所有t中所有字符呢?

开辟字符频数数组(创建字符频数数组,将字符的 ACSII 码作为数组的下标),用频数数组记录一下,每个字符中出现的字符个数。让右边界进来的时候,字符频数加 1,window[right]++;当左边界滑出的时候,字符频数减 1,window[left]--;

int[] window = new int[128]; //s字符串的滑动窗口频数数组
int[] pattern = new int[128];//t字符串的频数数组

2.    right 变长刚好满足条件,left 变短到刚好不满足条件,然后 right 变长刚好满足条件,如此循环下去,直到 right 到达末尾。

3.   循环不变量[left,right),强语义:滑动过程中right左边的数都是系统看到过的数,且right-left刚好等于数组长度。

3.    定义distance变量

由于我们并不关心字母的顺序,因此我频们采用的是对比数数组的方式。

先对 T字符串 做频数统计,然后设置一个变量 distance 表示 T 中一共有多少个的相同的字母;
left 和 right 在动的时候,只对 T 中出现的字母做统计。

窗口中单个字符的长度等于t中的字符时,distance我们不再增加

4.   窗口移动时的判断条件

当right向右滑动,且window[s[right]]<pattern[s[right]]时,distance+1

当left向右滑动,且window[s[left]]==pattern[s[left]]时,distance-1;

注意:当right向有移动的时候,超过t数组中的个数的时候   distance不再加一,同理,当left向左移动的时候只有window[schar[left]严格等于pattern[schar[left]]时,distance--;

代码实现

 public String minWindow(String s, String t) {
        //根据注意点可以完成前提定义变量
        char[] schar=s.toCharArray();
        char[] tchar=t.toCharArray();
        int slen=s.length();
        int tlen=t.length();
        if(slen==0||tlen==0||slen<tlen){
            return "";
        } 

        int begin=0;
        int distance=0;   //窗口中体重出现字符的个数  对应字符超出不做计算
        int minlen=slen+1;    //初始话为不可能存在的值

        //窗口位于【left,right) 
        int left=0;
        int right=0;
        
        //窗口频数数组
        int[] window=new int[128];
        int[] patten=new int[128];
        for(char c:tchar){
            patten[c]++;
        }

        //运行工作 这一步right向右移动
        while(right<slen){    //如果right<slen,则有right向右滑动的循环
            // 扫到t中不存在的字符时,即t字符中扫到schar【right】字符数==0,
            if(patten[schar[right]]==0){       
                right++;                //则right+1;跳过下面继续循环
                continue;
            }

            //扫到t中存在字符时,且判断条件满足
            if(window[schar[right]]<patten[schar[right]]){
                distance++;    
            }
            window[schar[right]]++;
            right++;
            
            //在这个循环下左边界滑动条件:两个字符数组频数相同的时候
            while(distance==tlen){
                
                //minlen的取值
                if(right-left<minlen){
                    minlen=right-left;
                    begin=left;
                }
                
                              
                //扫到t中不存在的字符时,即t字符中扫到schar【right】字符数==0,
                if(window[schar[left]]==0){
                    left++;
                    continue;
                }

                //当left向左移动的时候只有window[schar[left]严格等于pattern[schar[left
                //]]时,distance--;             
                if(window[schar[left]]==patten[schar[left]]){
                    distance--;
                }
                window[schar[left]]--; 
                left++;
                
            }   

        }
        if (minlen==slen+1){
                return "";
            }else {
                return s.substring(begin,begin+minlen);
            }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值