滑动窗口 -- 限制窗口内某元素的数量/种类

news2024/9/28 11:25:05

目录

长度最小的数组

题解:

将x减到0的最小操作数

题解:

 最大连续1的个数

题解:

 无重复字符的最长子串(限制数量)

题解:

水果成篮(限制种类)

题解:

找到字符串中所有字母异位词(限制数量和种类)

题解:

串联所有单词的子串

 题解:

最小覆盖子串

题解:


滑动窗口最重要的是如何调整窗口,如何调整 left 和 right!

长度最小的数组

209. 长度最小的子数组 - 力扣(LeetCode)

题解:

由于题目要求找满足条件的子数组,这个子数组可以看成一个区间,我们可以利用左右指针,左指针指向这个区间的开始位置,右指针指向区间的结束位置,寻找满足条件的区间的过程,可以形象地看成一个窗口在数组内滑动。

左指针设为 left,右指针设为 right,以示例 1 为例,起始时 left 和 right 都指向下标为 0 的位置,此时窗口内的数只有 2,明显窗口内的总和小于 target,所以我们让 right 右移,left 不动,不断扩大这个窗口,让窗口内的总和不断增大,如下图所示:

当窗口内的总和大于 target 时,需要暂停下来思考一下。

由于题目规定数组的每一个元素都是正整数,所以 right 继续右移,窗口的长度越来越长,窗口内的总和一定会越来越大,离正确答案越来越远!所以这个时候需要对窗口进行调整,怎么调整呢? 

调整的方法就是让 left 右移,让窗口缩小一点,那要缩小到什么时候就暂停呢?

当缩小到窗口内的和小于 target 时,left 就可以停止右移了,如果 left 继续右移,窗口内的和越来越小,又开始远离正确答案了。

如下图所示,当 left 右移到下标为 1 的位置时,窗口内的总和为 6,此时若 left 继续右移,窗口内的总和就越来越小了,离正确答案越来越远。

窗口调整完之后,right 就可以继续右移了,移动到窗口内的总和大于target 时,再调整一次窗口即可。

显然,当窗口内的总和等于 target 时,也需要停下来调整窗口,因为 right 继续右移,窗口内的总和又越来越大了,长度也越来越长,又开始远离正确答案了。

可以发现,当窗口内的总和大于等于 target 时,窗口的长度可能就是我们想要的答案,此时可以进行判断并更新结果。

总结:当窗口内的总和大于等于 target 时,我们就需要调整窗口的大小,在调整窗口前,更新一下答案。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum=0,ret=INT_MAX,n=nums.size();
        for(int left=0,right=0;right<n;right++)
        {
            sum+=nums[right];
            while(sum>=target)//注意题目是大于等于
            {
                ret=min(ret,right-left+1);
                sum-=nums[left];    ++left;
            }
        }
        return ret==INT_MAX?0:ret;
    }
};

将x减到0的最小操作数

1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

题解:

对于本题,将原数组掐头去尾,使去掉的数字的总和恰好为 x,换个角度想,假设数组的总和为 sum,将 x 减到 0 ,相当于让数组内的某个区间的总和为 sum-x,求出将 x 减为 0 的最小操作数,相当于求出使区间的总和为 sum-x 的最大长度,问题就转化为上一题的变形题,思路是类似的。 

如果 target = sum-x 的值小于 0 ,由于数组中的每个数都是正数,所以子数组的和不可能小于 0,所以子数组一定不存在。 

 注意如果数组内不存在这样的子数组,返回值为 -1.

在本道题中,我们假设 len 为窗口的长度,求出窗口的最大长度后,n - len就是将 x 减到 0 的最小操作数, len 的初始值应该设为多少呢?

如果设为 0,在leetcode 中,下面的数组是没办法通过的:

nums =  [8828,9581,49,9818,9974,9869,9991,10000,10000,10000,9999,9993,9904,8819,

  1231,6309] 

x = 134365

最终输出的结果为 -1,但实际答案为 16. 

为什么呢?nums 的总和恰好为 x,所以 target = sum - x = 0,这意味着初始时 right 和 left 指向同一个位置,tmp+=nums[ right ],  tmp > target  成立,进入while 循环, left 也右移 left 在 right 的下一个位置,且把 tmp 修正为 0,跳出了 while 循环,此时 tmp == target,进入 if 判断,更新 len 的长度,更新 len 的长度时,len 为 0,right - left+1 也为 0,len 更新后依然为 0

更新完 len 之后 right++,left 和 right 又指向同一个位置,重复上面的步骤。

 这就导致当 right 把数组遍历完了,窗口也结束了,但是 len 仍为 0,最终判断返回值时,len 为 0,返回 -1.

return len==0?-1:n-len;

所以应该把 len 的初始值设为 -1,这样在更新 len 的长度时,len 被更新为 0。

len=max(len,right-left+1);

 最后判断返回值时,len = 0 ,不等于 -1,n - len=n,也就可以求出正确答案!

return len==-1?-1:n-len;

正确答案: 

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum=0,n=nums.size();
        for(int i=0;i<n;i++) sum+=nums[i];

        int left=0,right=0,len=-1,tmp=0,target=sum-x;
        if(target<0) return -1;
        while(right<n)
        {
            tmp+=nums[right];
            while(tmp>target)
            {
                tmp-=nums[left];    ++left;
            }
            if(tmp==target)
                len=max(len,right-left+1);
            ++right;
        }
        return len==-1?-1:n-len;
    }
};

 最大连续1的个数

1004. 最大连续1的个数 III - 力扣(LeetCode)

题解:

这道题换个思路,就是求区间内 0 的个数不超过 k 的最大长度,用滑动窗口就可以解决,思路和前面一样!

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n=nums.size(),zero=0,left=0,right=0,ret=0;
        while(right<n)
        {
            if(nums[right]==0) ++zero;
            while(zero>k)
            {
                if(nums[left]==0) 
                    --zero;
                ++left; 
            }
            ret=max(ret,right-left+1);
            ++right;
        }
        return ret;
    }
};

 无重复字符的最长子串(限制数量)

3. 无重复字符的最长子串 - 力扣(LeetCode)

题解:

无重复字符,意味着字符的出现次数为 1。同样用滑动窗口来解决这个问题。我们可以用数组记录访问到的字符的出现次数。

当 right 不断右移时,如果 right 位置的字符的出现次数为 2,此时就需要维护窗口了,需要让 left 右移,找到 right 位置的字符第一次出现的位置,并跳过它。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ret=0,n=s.size(),left=0,right=0;
        //char hash[128]={0};用ASCII就可以
        int hash[128]={0};
        while(right<n)
        {
            hash[s[right]]++;
            while(hash[s[right]]>1)//找到重复的字符并跳过
            {
                hash[s[left]]--;
                left++;
            }
            ret=max(ret,right-left+1);
            ++right;
        }
        return ret;
    }
};

水果成篮(限制种类)

904. 水果成篮 - 力扣(LeetCode)

题解:

上一题要求没有重复的字符,即窗口内的字符只出现 1 次,限制字符的数量,这一题要求窗口内数字的种类只能为 2 种,即限制种类。

为什么需要用 map ,用 set 不可以吗?

当窗口内的数字的种类为 3 种时,我们需要维护窗口,即去掉其中一个种类,那怎么判断当前的窗口已经去掉一个种类了呢?用该种类在窗口中出现的次数来判断,让 left 不断右移,每右移一次,窗口内该字符出现的次数 -1,次数减到 0 时则说明该窗口已经没有这个数了!用 map 就可以实现这种索引的关系,set 则不行。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        unordered_map<int,int> hash;
        int n=fruits.size(),left=0,right=0,ret=0;
        while(right<n)
        {
            hash[fruits[right]]++;
            while(hash.size()>2)
            {
                hash[fruits[left]]--;
                if(hash[fruits[left]]==0)
                    hash.erase(fruits[left]);
                ++left;
            }
            ret=max(ret,right-left+1);
            right++;
        }
        return ret;
    }
};

找到字符串中所有字母异位词(限制数量和种类)

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

题解:

因为所给的字符都是小写字母,用数组就可以统计字符出现的次数(哈希表的消耗比较大)。

如何统计字符出现的数量?

窗口还没超的情况下,对于 right 访问的字符:

1、如果该字符在 p 中也出现过,且该字符在窗口内的数量还不够,则该字符为有效字符,count++;

2、如果该字符在 p 中没有出现过,则不做处理。

在代码中如何表示字符在 p 中也出现过,且该字符在窗口内的数量还不够?

如果字符 ch 在 p 中出现过,则 hash1[ ch-'a' ] 一定大于 0,如果 ch 在 p 中没有出现过,则 hash1[ ch-'a' ] 一定为 0。


每当 right 访问一个字符时,我们就统计 right 访问的字符出现的次数,即 hash2[ s[right] -'a'] ++,此时 hash2[ s[right] -'a'] >0,

  • 如果该字符在 p 中也出现过,则会有 hash2[s[right]-'a']<=hash1[s[right]-'a'],则 count++;
  • 如果该字符在 p 中不存在,则一定有 hash2[s[right]-'a'] > hash1[s[right]-'a'] =0,所以 count 不需要+1.
 if(hash2[s[right]-'a']<=hash1[s[right]-'a'])
     count++;

如果窗口超了,只需要跳过一个字符,跳过字符时需要判断这个字符是不是有效字符,是的话 count--,不是的话 count 不用修改,left 右移。

如果窗口内有效字符的个数等于 p 的长度,说明找到了一个起始索引。 

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int hash2[26]={0},hash1[26]={0},n=s.size(),m=p.size();
        for(auto x:p) hash1[x-'a']++;//统计p中每个字母出现的次数
        vector<int> ret;

        for(int left=0,right=0,count=0;right<n;right++)
        {
            hash2[s[right]-'a']++;
            if(hash2[s[right]-'a']<=hash1[s[right]-'a'])
                count++;//当前的字母为有效字母,且有效字符的个数还没达到,count++
                //如果为无效字母的话,hash1[s[right]-'a']=0,而hash2[s[right]-'a']>0,不会进入判断

            if(right-left+1>m)//窗口超了
            {
                if(hash2[s[left]-'a']<=hash1[s[left]-'a'])   
                    count--;
                hash2[s[left]-'a']--;
                ++left;//窗口右移
            }
            //窗口的长度和有效字符出现的次数相等
            if(count==m) ret.push_back(left);//找到了
        }
        return ret;
    }
};

串联所有单词的子串

30. 串联所有单词的子串 - 力扣(LeetCode)

 题解:

由于 words 中每个字符串的长度都是相同的,如果我们对 s 中的字符按照 words 中字符串的长度进行切割,就可以看作上一道题的变形。

我们可以把切分后的每组字符串看出一个整体, 相当于判断每一小组的字符串是否在 words 中出现过。

所以我们的工作就是切分并取出子串,可以用 substr 函数来取出子串,取出子串后的工作和上一题一样,判断是否为有效子串、数量是否匹配。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string,int> hash1;
        for(auto& x:words) hash1[x]++;//统计words中字符串出现的次数
        int len=words[0].size(),m=words.size();//字符串的长度
        vector<int> ret;
        for(int i=0;i<len;i++)
        {
            unordered_map<string,int> hash2;
            //注意right+len<=s.size()要取=
            for(int left=i,right=i,count=0;right+len<=s.size();right+=len)
            {
                string in=s.substr(right,len);//取出子串
                hash2[in]++;
                if(hash1.count(in) && hash2[in]<=hash1[in])  count++;//判断是否为有效子串
                if(right-left+1>m*len)//超出窗口了
                {
                    string out=s.substr(left,len);
                    if(hash1.count(out) && hash2[out]<=hash1[out])
                        count--;
                    hash2[out]--;
                    left+=len;
                }
                if(count==m) ret.push_back(left);
            }
        }
        return ret;
    }
};

最小覆盖子串

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

题解:

由于是覆盖,意思就是,在 s 字符串中,t 中出现字符的种类都要有,但每个字符出现的次数可以大于等于 t 中字符出现的次数。

设置变量 kinds,当某字符在 s 中出现的次数等于在 t 中出现的次数时,kinds ++,意思是这个字符已经覆盖完毕了。

当所有的字符都覆盖完毕时,就可以更新结果,并且调整窗口。

调整窗口,只需要让 left 右移,直到某个字符没有被覆盖,就可以让 right 右移,继续寻找下一个目标。

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128]={0},hash2[128]={0},kinds=0,begin=-1,minlen=INT_MAX;
        for(auto s:t) 
        {
            if(hash1[s]==0) kinds++;//统计字符的种类
            hash1[s]++;//统计字符出现的次数
        }

        for(int left=0,right=0,count=0;right<s.size();right++)
        {
            char in=s[right];
            hash2[in]++;
            if(hash1[in]==hash2[in]) count++;//字符出现的次数到了,有效字符的种类++
            
            while(count==kinds)//全部字符都凑齐了
            {
                if(right-left+1<minlen)//更新结果
                {
                    minlen=right-left+1;    begin=left;
                }
                char out=s[left];
                if(hash2[out]==hash1[out])  count--;//跳过当前字符后,种类-1
                //即在[left,right]区间内,字符出现次数不足
                hash2[out]--;
                left++;
            }
        }
        if(begin==-1) return "";
        else    return s.substr(begin,minlen);
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Skywalking告警配置

背景 skywalking 9.7.0&#xff0c;地址&#xff1a;Backend setup | Apache SkyWalking helm&#xff1a;skywalking-helm:4.5.0&#xff0c;地址&#xff1a;skywalking-helm/chart/skywalking/values.yaml at v4.5.0 首先来说一下为什么使用skywalking告警&#xff1f; …

[半导体检测-6]:为什么晶圆缺陷检测精度越高,所需要的光源的波长越短?

目录 前言&#xff1a; 1. 光束的聚焦能力 1.1 概述 1.2 光束的聚焦能力用什么指标来标识&#xff1f; 1. 光束质量因子&#xff08;M因子&#xff09; 2. 衍射极限倍数&#xff08;β因子&#xff09; 3. 斯特列尔比&#xff08;Strehl Ratio&#xff09; 4. 远场发散…

Spring6梳理13——依赖注入之引入集合Bean属性

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 13 依赖注入之引入集合Bean属性 13.1 创建Lesson类&#xff0c;student类和teacher实体类…

【LeetCode:2535. 数组元素和与数字和的绝对差 + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

jmeter性能测试---csv数据文件设置

&#xff08;1&#xff09;什么时候使用CSV数据文件设置&#xff1f; 当不同的用户&#xff0c;或者同一用户多次循环时&#xff0c;都可以获取到不同的值 &#xff08;2&#xff09;使用CSV数据文件设置进行参数化的步骤&#xff1f; 实例&#xff1a; 请求&#xff1a;htt…

基于Node.js+Express+MySQL+VUE实现的计算机毕业设计共享单车管理网站

单车信息选择骑行 骑行状态留言公告/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序 功能如下&#xff1a; 一、开发目标 在共享经济日益盛行的今天&#xff0c;共享单车作为一种绿色、便捷的出行方式&#xff0c;已经深入人们的日常生活。然而&#xff0c;随着共享…

短效IP是网络世界的神秘助力者

伙伴们&#xff0c;我们都知道网络世界神秘莫测&#xff0c;在当今这个高度数字化的时代&#xff0c;网络如同一张无形的大网&#xff0c;将人们的生活和工作紧密相连&#xff0c;成为不可或缺的一部分。而在这庞大的网络背后&#xff0c;有着很多挑战和危险&#xff0c;为了能…

ps快速更换电商图片背景,轻松变成白底图

前言 在电商领域&#xff0c;一张高质量的商品图片往往能吸引更多消费者的目光&#xff0c;提升商品的点击率和转化率。而白底图&#xff0c;以其简洁、清晰、专业的特点&#xff0c;成为电商平台上商品展示的首选。然而&#xff0c;传统的手动抠图方式不仅耗时耗力&#xff0…

Linux中部署Docker环境;Docker常用操作

一&#xff0c;部署Docker环境 官网手册&#xff1a;CentOS | Docker Docs 1.1、查看一下Linux内核版本 uname -r 要求3.10版本及以上。 2.2、卸载老版本docker&#xff0c;避免产生影响 如果服务器安装过docker&#xff0c;没有卸载再次安装会导致安装失败&#xff0c;首…

Latex和Vscode安装和配置

一、Latex安装教程 打开清华大学开源软件镜像站&#xff0c;下载texlive.iso文件 右键点击ios文件&#xff0c;点击装载 配置latex安装 4. 安装过程 二、VScode安装和配置教程 打开Vscode官网&#xff0c;下载安装包 2.右键&#xff0c;以管理员身份运行VSCode安装包&#…

Day.js时间插件的安装引用与常用方法大全

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

C++ 数据类型分类

在C中&#xff0c;数据类型可以大致分为内置类型&#xff08;Built-in Types&#xff09;、标准库类型&#xff08;Standard Library Types&#xff09;和自定义类型&#xff08;User-Defined Types&#xff09;三大类。 内置类型&#xff08;Built-in Types&#xff09; 内置…

Kafka和RabbitMQ比较

Kafka和RabbitMQ都是流行的消息队列系统&#xff0c;它们在分布式系统中扮演着至关重要的角色&#xff0c;用于异步消息传递和解耦应用组件。尽管它们共享一些基本的概念&#xff0c;但它们在设计目标、性能特性、使用场景等方面有着显著的差异。 设计目标 Kafka&#xff1a;Ka…

理解Java引用数据类型(数组、String)传参机制的一个例子

目录 理解Java引用数据类型&#xff08;数组、String&#xff09;传参机制的一个例子理解样例代码输出 参考资料 理解Java引用数据类型&#xff08;数组、String&#xff09;传参机制的一个例子 理解 引用数据类型传递的是地址。用引用类型A给引用类型B赋值&#xff0c;相当于…

ERROR:start workflow error,dolphinscheduler log重复刷屏(死循环)直至磁盘存满

在使用ds过后发现&#xff0c;我虚拟机中的磁盘内存全部沾满了 查看目录下大于100M的文件&#xff1a; find / -size 100M 查看后发现问题在于ds产生的日志文件特别大而且多&#xff0c; 查看日志后发现日志中一直都在死循环错误&#xff1a;start workflow error 等 其中文件…

【论文_1992】 REINFORCE » P2 附录

Williams, R. J. Simple statistical gradient-following algorithms for connectionist reinforcement learning. Mach. Learn., 8:229–256, 1992. PDF 下载链接 前面部分&#xff1a;【论文_1992】 REINFORCE P1 文章目录 附录 AA.1. REINFORCE 算法的一些结论A.2. 回合式 …

《深度学习》迁移学习综合应用 原理、案例解析与实现

目录 一、迁移学习 1、什么是迁移学习 2、迁移学习步骤 1&#xff09;选择预训练的模型和适当的层 2&#xff09;冻结预训练模型的参数 3&#xff09;在新数据集上训练新增加的层 4&#xff09;微调预训练模型的层 5&#xff09;评估和测试 二、案例实现 1、数据准备…

内网穿透的应用-Windows系统安装SeaFile并实现远程访问本地共享文件资料详细教程

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 下载安装3.2 Cpolar注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 本文主要为大家介绍&#xff0c;如何使用两个简单软件…

如何使用ssm实现基于BS的库存管理软件设计与实现+vue

TOC ssm708基于BS的库存管理软件设计与实现vue 绪论 课题背景 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化。目前&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到…

【Python报错已解决】ModuleNotFoundError: No module named ‘psutil’

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…