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

目录

长度最小的数组

题解:

将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.xdnf.cn/news/1547326.html

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

相关文章

Studying-图论包含的算法总结

目录 1.DFS&#xff08;深度优先搜索&#xff09; 代码框架&#xff1a; 2. BFS&#xff08;广度优先搜索&#xff09; 代码框架&#xff1a; 3. 并查集 4.最小生成树之Prim 5.最小生成树之Kruskal 6.拓扑排序 7. 最短路径之-dijkstra&#xff08;朴素版&#xff…

淘宝霸屏必备工具:淘宝商品评论电商API接口

淘宝商品评论电商API接口是指用于获取淘宝商品评论信息的一种接口&#xff0c;通过该接口可以获取淘宝网上商品的评价内容、评价等级、评价数量等信息。通过了解并使用该接口&#xff0c;能够帮助电商了解消费者对商品的评价情况&#xff0c;做好商品的推广和销售工作。 接口使…

Leetcode - 周赛416

目录 一&#xff0c;3295. 举报垃圾信息 二&#xff0c;3296. 移山所需的最少秒数 三&#xff0c;3297. 统计重新排列后包含另一个字符串的子字符串数目 I 四&#xff0c;3298. 统计重新排列后包含另一个字符串的子字符串数目 II 一&#xff0c;3295. 举报垃圾信息 本题就是…

消息号 FS215 对科目 2221010200 7333允许销项税, J1 不允许

业务场景&#xff1a; 在做发票校验时&#xff0c;报错“消息号 FS215 对科目 2221010200 7333允许销项税, J1 不允许”而且计算税额失效&#xff0c;红灯报错。 初步怀疑是税码配置问题 FTXP J1是进项税&#xff0c;但是这里维护了销项税和均一税&#xff0c;在这里删除是需…

SQLSERVER通过触发器限制客户端IP地址

方法一&#xff1a;SQL Server 2005 SP2或更高版本(触发器) 当SQL Server 2005升级到SP2或者更高的版本的时候&#xff0c;还可以通过新增的触发器来实现控制。 执行下面的T-SQL后&#xff0c;将使除IP地址为192.168.1.1之外的客户端连接失败。 USE master; GO CREATE TRIGGE…

CMA软件检测机构人员职责分类、要求、档案资料

一、CMA软件检测机构人员职责分类&#xff1a; 1、最高管理者 2、授权签字人 3、技术负责人 4、质量负责人 5、软件测试人员 &#xff08;从事软件测试项目管理、测试需求分析、测试策划和测试设计活动的人员、软件测试执行人员&#xff09; 6、报告编制员 7、报告审核…

革新体验:细数3D在线预览在多个行业的广泛应用

‌3D在线预览展示技术的应用领域非常广泛&#xff0c;涵盖了从电子商务、产品设计、建筑设计到文化遗产保护等多个方面。‌ ‌1、电子商务‌&#xff1a; 在电商领域&#xff0c;3D展示技术为商品提供了全方位的展示&#xff0c;包括产品的外观、功能和卖点。这种交互式的购物…

【Docker】01-Docker常见指令

1. Docker Docker会下载镜像&#xff0c;运行的时候&#xff0c;创建一个隔离的环境&#xff0c;称为容器。 docker run -d \ # 创建并运行一个容器&#xff0c;-d表示后台运行 --name mysql \ # 容器名称-p 3307:3306 \ # 端口映射&#xff0c;宿主机端口映射到容器端口-e TZ…

buuctf [ACTF2020 新生赛]Include

学习笔记。 开启靶机。 进入靶场&#xff1a; 我们跟进 tips瞅瞅&#xff1a; 额&#xff0c;纯小白&#xff0c;能想到的就是先F12看看&#xff0c;在CTRLu、以及抓包。 得&#xff0c;不会了&#xff0c;看wp呗&#xff0c;不会死磕没脑子0,0&#xff1f; 参考&#xff1a;…

如何在 VitePress 站点中集成 Gitalk 评论插件及其关键注意事项

你好&#xff0c;我是陈明勇&#xff0c;一名热爱技术、乐于分享的开发者&#xff0c;同时也是开源爱好者。 成功的路上并不拥挤&#xff0c;有没有兴趣结个伴&#xff1f; 个人网站&#xff1a;https://chenmingyong.cn 文章持续更新&#xff0c;如果本文能让您有所收获&#…

OJ在线评测系统 后端 用策略模式优化判题机架构

判题机架构优化(策略模式) 思考 我们的判题策略可能会有很多种 比如 我们的代码沙箱本身执行程序需要消耗时间 这个时间可能不同的编程语言是不同的 比如沙箱执行Java要额外花费2秒 我们可以采用策略模式 针对不同的情况 定义不同独立的策略 而不是把所有情况全部放在一个i…

二分图算法模板以及简单应用

染色法判断二分图 给定一个 n 个点 m 条边的无向图&#xff0c;图中可能存在重边和自环。 请你判断这个图是否是二分图。 输入格式 第一行包含两个整数 n 和 m。 接下来 m 行&#xff0c;每行包含两个整数 u 和 v&#xff0c;表示点 u 和点 v 之间存在一条边。 输出格式 …

Matplotlib | 一文搞定Matplotlib从入门到实战演练!

文章目录 1 什么是Matplotlib1.1 Matplotlib的安装1.2 Matplotlib的基本使用 2 绘制直线3 绘制折线设置标签文字和线条粗细设置中文标题风格的设置 4 绘制曲线绘制曲线yx^2绘制正弦曲线和余弦曲线画布分区 5 绘制散点图绘制不同种类不同颜色的线 6 绘制条形图&#xff08;柱状&…

1. Linux系统(CentOS7.9)安装

toc 一、Linux概述介绍 1、Linux系统介绍 Linux, 一类操作系统的统称 部署在服务器上&#xff0c;部署项目、应用 服务器: 硬件设备, 柜式服务器&#xff0c;(华为、浪潮、联想) 提供服务的机器 2、Linux的优势 开源, open source , 开放源代码稳定性最大化发挥硬件资源 …

【电子通识】案例:连接器接线顺序评估为什么新人总是评估不到位?

在一个IC卡切换的工装板(一切多)中,设计需求是一张PCB(充当活动卡片)插入读卡器,将卡片中的所有信号引出通过连接器连接到后级设备。 比如下图所示是一种IC卡压力测试设备,使用钢片卡片将压力信号通过连接器引入测试设备。 最后根据ISO/IEC 7816-2标准中我们看到…

Mortise AI编程智能体产品 | OPENAIGC开发者大赛企业组AI创作力奖

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…

c++ 杂项

简答题 1、什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数是在类中定义函数时&#xff0c;在函数前加 virtual 关键字。父子类中只有一个该函数。 如果子类中没有重写该虚函数。那么父子类空间中使用的都是父类定义的该函数。 如果子类中重写了该虚函数&#xff…

Leetcode面试经典150题-322.零钱兑换

给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无限的。 示…

9.26作业

C 面试题 1,什么是虚函数?什么是纯虚函数? 虚函数&#xff1a;父子类中&#xff0c;在父类中的函数需要在子类中进行重写&#xff0c;重写后父子类空间中使用的都是重写后的函数&#xff0c;该函数就是虚函数&#xff0c;虚函数的声明需要在函数前加virtual。 纯虚函数&…

从自身经历浅谈对于C++/Java的认识

1.声明 因为一些其他的原因&#xff0c;我决定从C转到java方向学习&#xff0c;后期可能就要换方向了&#xff0c;以后主要学习这个java相关的这个技术了&#xff0c;起码暂时不会学习这个C里面的内容了&#xff1b; 2.我的感慨 当时选方向的时候&#xff0c;我自己就是选的…