滑动窗口问题

本文探讨了滑动窗口算法在LeetCode题目中的应用,分别通过找到字符串中的所有字母异位词(力扣438)和至少有k个重复字符的最长子串(力扣395)来阐述定长与不定长滑动窗口的解题思路。在例一中,利用定长滑动窗口判断子串中字符出现次数;而在例二中,面对不定长窗口,需要限制窗口内字符种类和出现次数,并动态调整窗口位置。

滑动窗口(定长与不定长)

例一:找到字符串中的所有字母异位词(力扣438)

找到字符串中的所有字母异位词
题目描述:
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
在这里插入图片描述

题目分析:异位词指 相同字母重排,即在一个字符串里面只要各个字母出现的次数相同即可,与出现顺序无关。所有我们只需要判断s中和p长度相同的一段子串里面各个字母出现的次数和p中是否相同即可。这里就会用到一个定长滑动窗口的思想。在s中从开头有一个长度和p一样的窗口,不断向后滑动进行判断,直到到达s的末尾。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int m=s.size();
        int n=p.size();
        if(m<n)return {};
        vector<int>window(26,0);
        vector<int>pt(26,0);
        for(int i=0;i<n;i++){
            pt[p[i]-'a']++;//统计p中每个字母出现的次数
        }
        vector<int>result;//用于存储满足条件的字符串的起始下标
        int left=0;//窗口的左端
        for(int right=0;right<m;right++){//窗口右端,滑动窗口的长度一直保持为p的长度
            window[s[right]-'a']++;//统计窗口内各个字母出现的次数
            if(right>=n){//某个定长窗口内不满足条件则整个窗口向右移动
                window[s[left]-'a']--;
                left++;
            }
            if(pt==window){
                result.push_back(left);
            }
        }
        return result;
    }
};

例二:至少有k个重复字符的最长子串(力扣395)

至少有k个重复字符的最长子串
题目描述:给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。
在这里插入图片描述
题目分析:这个题和上一个题思路有些相似,需要统计字母出现的次数,但是也有很多不一样的地方。上一个题窗口的长度是固定的,我们只需要向后滑动判断即可。这个题窗口的长度不确定,而且也不光是判断窗口中出现的各个字符的次数。
思路:1、限制窗口中出现的不同的字符的个数(从1到26,进行26次循环)
2、限制次数后窗口右端不断向后移,统计不同字符的个数即出现次数大于k的字母个数。当满足限制的字符个数等于窗口内的字母种类数等于窗口中出现次数大于等于k的字母数,则改子串符合条件,将子串长度存储并在下一次进行更新。
3、若向右移动的过程之中窗口里出现的字母种类数超过了我们限制的个数,窗口左端向右移动,直至满足条件。
4.不断进行上述操作,直到找完所有情况。

class Solution {
public:
    int longestSubstring(string s, int k) {
            int result=0;
            int a[26];//统计每个字符出现的次数
            for(int i=1;i<=26;i++){//滑动窗口中可以出现的字符种类个数依次递增
                memset(a,0,sizeof(a));//给数组a进行初始化,
                int left=0,right=0;//滑动窗口的左右两边
                int diff_count=0,count=0;//diff_count表示窗口中出现大的不同的字符的个数,count表示出现次数大于等于k的字符个数
                while(right<s.length()){
                    int add_index=s[right]-'a';
                    a[add_index]++;;
                    if(a[add_index]==1)
                    diff_count++;
                    if(a[add_index]==k)
                    count++;
                right++;//向右增大窗口长度
                while(left<right&&diff_count>i){
                    int del_index=s[left]-'a';
                    if(a[del_index]==k)
                    count--;
                    if(a[del_index]==1)
                    diff_count--;
                    a[del_index]--;
                    left++;//滑动窗口左侧向右移动,缩小窗口长度
                }
                if(diff_count==i&&diff_count==count)
                result=max(result,right-left);
            }
        }
            return result;
    }
};

总结:定长的滑动窗口问题比较简单,但是不定长问题里面的情况比较绕,需要仔细,理清思路。

滑动窗口是一种常用的算法技巧,在并行操作中也有广泛应用。从提供的引用来看,虽未直接提及并行操作下滑动窗口问题的内容,但可从常规滑动窗口原理和解决思路进行一定推导。 ### 原理 在常规场景下,滑动窗口原理在TCP中有体现,TCP窗口机制有固定窗口大小和滑动窗口两种。发送方和接收方分别保持一个窗口,发送方只有落在发送窗口内的数据帧才允许被发送,接收方只有落在接收窗口内的数据才会被接收,通过改变发送窗口和接收窗口的大小实现流量控制[^5]。在并行操作中,其原理可类比为在多个数据序列或任务流上同时维护多个窗口,每个窗口独立或协同地在数据上滑动,以满足特定条件。例如在多个线程或进程中同时处理不同的数据块,每个线程或进程可以维护一个滑动窗口来处理其负责的数据。 ### 解决方法 - **模板化方法**:对于普通滑动窗口问题有通用模板,定义滑动窗口的左右下标,通过循环判断条件来移动左右指针,同时在合适的时候做除指针移动之外的其他操作。在并行操作中,可以为每个并行任务分配一个这样的窗口,每个窗口独立执行该模板逻辑。如在多个线程中,每个线程负责一个数据子集,使用如下类似代码模板: ```java class Solution { public int minSubArrayLen(int s, int[] a) { int l = 0, r = -1; // 定义滑动窗口两下标 while (l < a.length) { if (r + 1 < a.length && 另一个条件) { // r+1要小于输入长度 // 只做l++ 或者 ++r相关操作 } else { // 只做l++ 或者 ++r相关操作 } // 做除了l++ 或者 r++之外的其他操作 } } } ``` - **逆向思维与条件判断**:在一些问题中可以采用逆向思维,将问题转换为更易处理的形式。如将找到满足特定和的最小子数组问题转换为找到最大连续子数组使其和为特定值的问题。在并行操作中,每个并行任务可以独立判断窗口内的条件是否满足要求,若不满足则移动窗口指针。同时还需要注意特殊情况的判断,避免出现死循环等问题[^2]。 - **窗口指针移动规则**:左指针的功能是用来缩小窗口的,当窗口内的条件已满足题目条件或多于题目条件时,缩小窗口,即左指针右移直到窗口条件不满足为止,记录当前窗口大小并更新满足条件的最小窗口记录,之后再次扩展右指针使窗口满足条件。在并行操作中,每个并行任务的窗口都遵循此规则移动指针[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值