滑动窗口训练9.21

news2025/1/14 2:32:25

好久没有写博客了,自从上半年蓝桥杯结束后,就有点懈怠了

最近两三周才又慢慢刷起题来,也顺便记录下自己的成长!

今天是滑动窗口的章节,前两周刷了字符串、双指针、模拟。这些板块我都在leetcode上找了些题,并且每个板块大概刷了两三天,其中包括了从前学习这些东西所做过的题,来复习一遍,和找新的题,以前的题有一半都做不上来了。。

蛮久不刷确实会掉题感啊,闲话少叙,开始今天的新内容

219.存在重复元素II

219. 存在重复元素 II - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/contains-duplicate-ii/?envType=list&envId=24zW97w8第一题,是个简单题,是昨天刷的,虽然是简单题,但是这两天刷滑动窗口给我的感觉是,滑动窗口虽然是双指针的变体,但是还是比想象中要难得多

这道题要求是找到两个数,这两个数相等的前提下,它们的下标值不能大于k

先是我自己写的代码:

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        int i=0,j=0;
        if(nums.size()==1)return false;
        unordered_set<int>hset(nums.size());
        for(i=0;i<nums.size();++i){
            if(j>0)hset.erase(nums[i-1]);
            while(j<nums.size()&&!hset.count(nums[j])&&abs(j-i)<=k){
                hset.insert(nums[j]);j++;
            }
            //跳出循环有两种可能,一种是找到相同字符,一种是超过k
            if(j<nums.size()&&hset.count(nums[j])&&abs(j-i)<=k)return true;
            if(j==nums.size())return false;
        }
        return false;
    }
};

这段代码思路就是,不停的移动右窗口,直到右窗口到达边界或者找到了两个相等的数据,又或者当前窗口已经大于k了。那么我们就停下来,去判断是何种原因导致的停下循环,如果是在右边界没出界的前提下,并且找到了相等数据而且下标差还不大于k,那么直接返回true

如果右边界出界直接返回false,因为我们设置的是此时右窗口不会向前走只能向后走

如果既没有到达右边界,又没有相等数据,那只能说明出k范围了,这时我们直接删除当前窗口左元素,让左窗口右移即可!

滑动窗口的题有很多都要用到哈希,什么样的题要用滑动窗口呢?
要求字符串或者数组中的某一部分的数据,连续子串类型的
用两个指针去分割这个字串,所以形象的称为滑动窗口
什么时候搭配哈希?需要判断字符是否重复或者需要判断一个子串中不相邻的某两个元素之间的关系,用哈希存储窗口内的数据,以便查重
解题思路:
使用哈希表set来存储滑动窗口的数字,如果满足新数字和窗口里任何数字都不相等且在k范围内条件则加入set
否则判断如果在k内且有数字相等,则说明找到了正确答案

再看官方答案,官方答案也是用set,但是比我们简洁的多

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_set<int>set;
        for(int i=0;i<nums.size();i++){
            if(i>k)set.erase(nums[i-k-1]);
            if(set.find(nums[i])!=set.end())return true;
            set.emplace(nums[i]);
        }
        return false;
    }
};

 思路是相同的,不过它不用左右边界去记录窗口,只用i,相当于单指针,当i大于k说明此时每次都需要减少左窗口的值,用i-k-1即可,然后去查找,因为先判断的是否两数据相减超出k所以我们直接看是否能找得到,能找得到就直接回true就完事了,这种方法也是保证了窗口一直保持最大时候,去删除左数据。

再看卡尔哥的题解

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int,int>map;
        for(int i=0;i<nums.size();i++){
            if(map.find(nums[i])!=map.end()&&i-map[nums[i]]<=k)
            return true;
            map[nums[i]]=i;
        }
        return false;
    }
};

用map是映射数据结构的优点,键存储当前数字,值存储当前下标,写法很直观,通俗易懂整体思路和前面的一样


643.子数组最大平均数I

643. 子数组最大平均数 I - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/maximum-average-subarray-i/?envType=list&envId=24zW97w8求的是在一个字符串里找到平均数最大且长度为k的子数组

也是先看自己写的思路:

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
       double res=INT_MIN;int left=0;double sum=0;
        for(int i=0;i<nums.size();i++){
            if(i>k-1){sum-=nums[left++];}
            sum+=nums[i];
            if(i>=k-1)
            res=max(res,sum/k);
        }
        return res;
    }
};

用一个变量来记录每一次当前范围子数组的和,然后每次遍历都求一次平均数

判断当前窗口是否大于k,大于则减去左窗口的值,这里的判断就明显整洁了不少吧,这是上一道题吸收来的经验。

这里也可以写成if(i>=k)sum-=nums[i-k]这样就不需要有left来记录左边界了
i控制右窗口,left控制左窗口
到了最大窗口数sum每次会减少相应的值,在循环中不停变化平均值,最后返回平均值即可
需要注意的是不是每一次都要取平均值,只有当i走到合适的位置后
也就是说只有当窗口大小等于k时候才取

再来看看官方题解

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        int sum=0,maxsum=0;
        for(int i=0;i<k;i++)sum+=nums[i];
        maxsum=sum;
        for(int i=k;i<nums.size();i++){
            sum-=nums[i-k];sum+=nums[i];
            maxsum=max(sum,maxsum);
        }
        return 1.0*maxsum/k;
    }
};

思路也是滑动窗口
但是时间明显少了一些
这可能是因为它只求了一次平均值
做法就是,先循环,让窗口填满记录一下当前和
一开始先加数加到k,然后右窗口向前走一次,左窗口便向右走一次,
从窗口下一个位置遍历,sum加上新数,并且减去旧窗口数(最左边的)
使用i-k,这一点十分巧妙
然后最后答案返回遍历过程中k个连续子串的最大和
取平均数


1763. 最长的美好子字符串

1763. 最长的美好子字符串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-nice-substring/?envType=list&envId=24zW97w8在给定字符串中寻找子串,子串满足若出现某个字母,则该子串中必须含有其对应的大写/小写字母,数量不限,比如说Aaa这也是可行的

一开始我的思路是用哈希表存窗口的字母,并且把它变成对应的大小写字母,如果再看到则删掉哈希存储的字符,然后判断当前窗口的哈希表是否存在数据,如果存在则说明该字串有一些不成对的字母,若没有可以比较是否为最长,然后更新结果值

后来才觉得,想简单了,因为Aaa也是可行的

然后就想不出来其他解法了看了题解

class Solution {
public:
    string longestNiceSubstring(string s) {
        if(s.size()<2)return "";string res;
        for(int i=2;i<=s.size();i++){
            for(int j=0;j+i-1<s.size();j++){
                string str=s.substr(j,i);
                if(str.size()>res.size()&&fun(str))res=str;
            }
        }
        return res;
    }
    bool fun(string&s){
        int AA[26]={0};int aa[26]={0};
        for(char c:s){
            if(c-'A'<=26)AA[c-'A']++;
            else aa[c-'a']++;
        }
        for(int i=0;i<26;i++){
            if(aa[i]>0&&AA[i]==0||aa[i]==0&&AA[i]>0)return false;
        }
        return true;
    }
};

题解的这种方法很好也容易理解
它是外层循环规定此时最大窗口为多大,然后内层循环从字符串第一个字符往后走,依次增大,窗口也随j移动
内层循环判断如果此时为完美子串,且当前字串大,那么给res
如何判断完美子串?
函数使用两个数组充当哈希表,一个存大写字母,一个存小写字母
将当前j位置的窗口子串截取下来,通过遍历向两数组填数
遇到对应字符相应位置自增
然后填数完毕,判断如果对应位置大小写字母数组都大于0则说明是完美字串
这里我的理解是,如果不写自增,只是用等于1来标记大/小字母出现过,也是可以的
而且那个双for循环,假如写成i控制字符串起始位置
然后j控制子串结束位置,也相当于在字符串各个位置使用不同窗口进行截取
效果应该是一样的
重要的不是这点,重要的是如何判断完美字串
使用两个数组记录,对应位置是否出现的思想非常重要!

遇到不能用一个哈希表解决的滑动窗口问题时,可以思考能不能用两个哈希表解决它们


395.至少有k个重复字符的最长子串

395. 至少有 K 个重复字符的最长子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-substring-with-at-least-k-repeating-characters/?envType=list&envId=24zW97w8个人认为很难想思路,官方题解我看不懂,吐槽一下官方题解通常都有点晦涩难懂

所以我参考了其他用户高手

我自己当然也有尝试解题,思路是有的,也实现了,但是就是超时

class Solution {
public:
    
    int longestSubstring(string s, int k) {
        int res=0;
        for(int i=0;i<s.size();i++){
            for(int j=i;j<s.size();j++){
                if(fun(s,i,j,k)&&res<j-i+1)res=j-i+1;
            }
        }
        return res;
    }
    bool fun(string &s,int left,int right,int k){
        int arr[26]={0};
        for(int i=left;i<=right;i++)arr[s[i]-'a']++;
        for(int i=0;i<26;i++){
            if(arr[i]!=0&&arr[i]<k)return false;
        }
        return true;
    }
};

我对这个代码做了很多优化,比如把哈希表从map改成数组,这样可以免去insert的时间消耗,

把使用substr截取字符串改成传下标,个人认为代码还是十分简洁的,不过双for循环就是容易超时,没有办法。。。

class Solution {
public:
    
    int longestSubstring(string s, int k) {
        int res=0;
        for(int i=0;i<s.size();i++){
            for(int j=i;j<s.size();j++){
                if(fun(s,i,j,k)&&res<j-i+1)res=j-i+1;
            }
        }
        return res;
    }
    bool fun(string &s,int left,int right,int k){
        int arr[26]={0};
        for(int i=left;i<=right;i++){
            if(arr[s[i]-'a']>=k)continue;
            arr[s[i]-'a']++;
        }
        for(int i=0;i<26;i++){
            if(arr[i]!=0&&arr[i]<k)return false;
        }
        return true;
    }
};

这是人家写的代码,时间复杂度为ON

循环是以26个不同的字母为限制的,即每次只允许窗口内存在n个不同的字母
设置变量来分别存放当前窗口内不同种类字符的个数、符合该种字符个数等于k的字符有多少个、窗口左边界、窗口右边界
变量设置好后,开始进入第一个while,它是扩张右窗口的
如果当前数组位置为1,diff++,说明有不同的字符第一次加进来
如果当前位置数值为k则count++说明当前字符出现次数第一次达到k,注意这里并不是大于等于k时候count++,这样的话后续加进来该字符会导致重复计数
如果当前窗口出现的字符种类大于i,进入左边界缩小的阶段,进入的是循环而不是if判断语句
循环中如果左边界在右边界左侧,说明该窗口有缩减的必要,并且还要保证此时窗口字符种类大于i
进入之后是和上面类似的判断,不停增大left直到字符种类在允许的范围内
值得注意的有两点:
第一:判断窗口种类个数是否超过i的循环,应该在扩大右边界循环内部,每扩大一次右边界,并完成相应变量增加后,就立即判断此时左边界是否应该缩小
第二:判断完左右边界之后,立即进行的是一句判断
如果当前所允许的字符种类个数等于当前窗口的字符种类个数,并且窗口里任意字符的个数都大于或等于k,则判断是否应该更新答案值
为什么要判断现在最大允许的字符种类个数呢?
因为该循环是由1开始到26结束,每次判断的是最大允许范围这能保证当前窗口前提下,找到的是最大的数值,这样才可能需要更新数据
一定要注意不要把更新数据这条语句不小心写到循环外面了,这样如果在窗口移动时有新数据,就添加不上了
 


以上便是今天滑动窗口练习的全部内容,感觉有帮助的小伙伴们可以三联支持下,现在正是需要涨粉的时候2333

如果大家想看其他专栏的,比如动态规划、哈希表、贪心算法或是其他数据结构可以看往期文章

最近打算更一些更偏重基础的内容,以巩固基础,大家可以多多讨论,或者有想看的题解(当然不要太难)也可以评论发出来大家讨论,尽量做到日更吧

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

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

相关文章

【大学英语视听说上】Topic Presentation

&#xff08;一些视听说的必要作业...&#xff09; 展示&#xff0c;每人准备ppt文件&#xff0c;时长五分钟&#xff0c;第一分钟自我介绍&#xff0c;之后四分钟介绍一个主题。 例如&#xff1a;中秋节&#xff0c;英国地标建筑等等。 要求图文并茂&#xff0c;发音清楚标…

Intel汇编在VS下开发的环境配置

1. 创建一个C/C的空项目 2. 创建汇编源码文件, 就是C文件改后缀为asm 3. 在生成依赖项一栏中选择自定义 4. 选择masm 5. 在源文件上右击选择属性 6. 这么设置一下 7. 为了让代码看的更舒服一些, 添加一些高亮插件 8. 安装AsmHighligher和AsmDude插件(非必须), 其中前者主要是高…

RK3568驱动指南|第五期-中断-

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

债券风险价值类

声明 本文是学习GB-T 42815-2023 债券价格指标产品描述规范. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了债券价格指标产品各要素的定义、范围、框架及输出口径。 本文件适用于债券价格指标产品的编制发布机构及使用机构。 …

Rsync学习笔记1

企业架构Web服务器的文件及时同步&#xff1a; 1&#xff09;能够理解为何要服务器的文件同步&#xff1b; 2&#xff09;能够简单描述实现文件同步的几种方式&#xff1b; 3&#xff09;能够实现服务器文件实时同步的案例&#xff1b; 服务器同步文件的必要性&#xff1a; …

求二维子数组的和(剖析)

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;本篇剖析一下二维子数组求和规则&#xff1a; &#x1f412;个人主页 &#x1f3c5;JavaSE系列专栏 &#x1f4d6;前言&#xff1a;本篇剖析一下二维子数组求和 规则&#xff1a; 这…

阿里员工曝光,跳槽提供流水,将28K改成38K,成功率高吗?

在这位员工的曝光中&#xff0c;他声称通过提供虚假简历&#xff0c;将自己的工作经验和技能水平夸大&#xff0c;以获得更高的薪资。此外&#xff0c;他还提供了虚假的流水&#xff0c;使自己的收入看起来更高。然而&#xff0c;这一行为无疑是违反道德和诚信原则的&#xff0…

向表中针对全部列插入数据

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法格式: insert into 表名 values(); 我们来查看test01表里面有几列 mysql> show databases; -------------------- | Database …

学习记忆——宫殿篇——记忆宫殿——记忆桩——学校

教室 桶 走道 桌子 暖气 窗台 后背 窗帘 监视器 白盒子 教师 讲台 表 投影仪 音响 窗 喇叭 黑板 门 栏杆 椅子 食堂 桶 刷卡器 柱子 桌子 风扇 灯罩 一列椅子 地面 大门空间 电视 活动室 盘子 纸盒 油桶 称 水桶 展牌 帘子 消防栓 毯子 储物箱 宿舍 梯子 坐垫 挂件 吊兰 君子…

zabbix学习3--zabbix6.x-proxy

文章目录 proxy proxy # 安装mysql 8.0# 获取源码包【https://www.zabbix.com/cn/download_sources】 mkdir -p /data/zabbix_proxy/{data,install,logs,php} mkdir -p /var/run/zabbix_proxy tar xf zabbix-6.4.3.tar.gz -C /data/zabbix_proxy/install/ cd /data/zabbix_pro…

华为坤灵再上新,助力中小企业转型“易”见未来

中小企业&#xff0c;堪称国民经济发展的毛细血管&#xff0c;数量众多、分布广泛却又无比重要。 随着数字经济成为各行各业的主战场&#xff0c;数字化转型已是中小企业打开高质量发展之门的那把关键钥匙。《数字中国建设整体布局规划》就明确指出&#xff0c;推动数字技术和…

argparse的用法

目录 一、使用argparse 二、参数详解 参考 一、使用argparse argparse 模块是 Python 内置的用于命令项选项与参数解析的模块&#xff0c;argparse 模块可以让人轻松编写用户友好的命令行接口&#xff0c;能够帮助程序员为模型定义参数。 argparse定义包括四个步骤&#xff…

线路中故障电弧产生了应该如何治理?-安科瑞黄安南

故障电弧的危害 故障电弧是指由于电气线路或设备中绝缘老化破损、电气连接松动、空气潮湿、电压电流急剧升高等原因引起空气击穿所导致的气体游离放电现象。故障电弧发生时&#xff0c;其中心温度可高达3000 ℃左右&#xff0c;并伴随有金属喷溅物&#xff0c;足以引燃任何可燃…

【AD】【规则设置】关于绿色报错的消除

关于绿色报错的消除 1、打开 在上面工具栏的 Tools - Design Rule Checker2、这两列&#xff0c;分别右键 选择 Batch DRC - All Off 取消掉所有的打钩3、再点击左侧的Electrical 把右边的这几个都打钩 每天进步一点点 如果我的学习记录有帮到你&#xff0c;可否赏点买辣条的钱…

提高接口自动化测试效率:使用 JMESPath 实现断言和数据提取!

前言 做接口自动化&#xff0c;断言是比不可少的。如何快速巧妙的提取断言数据就成了关键&#xff0c;当然也可以提高用例的编写效率。笔者在工作中接触到了JMESPath&#xff0c;那到底该如何使用呢&#xff1f;带着疑惑一起往下看。 JMESPath是啥&#xff1f; JMESPath 是一…

安达发APS|生产计划部门如何提升产量?

在当下制造业中&#xff0c;生产计划的制定和执行对于提高产量、降低成本、保证交货期等方面具有重要意义。随着科技的发展&#xff0c;越来越多的企业开始使用APS生产排程软件来优化生产计划&#xff0c;提高生产效率。本文将从以下几个方面介绍如何利用APS生产排程软件提升产…

外滩大会观察|重估蚂蚁!

点击关注 文丨刘雨琦&#xff0c;编丨王一粟 “不同品种的小狗排队通过‘实名认证’、刷脸识别不仅能识别人&#xff0c;还能识别狗”。“人脸、虹膜、掌纹都已经不算黑科技&#xff0c;脑纹识别才是真的不可替代。” 2023年上海外滩大会上&#xff0c;6G、无人安全检测实验室…

学Python的漫画漫步进阶 -- 第十四步.网络通信

学Python的漫画漫步进阶 -- 第十四步.网络通信 十四、网络通信14.1 基本的网络知识14.1.1 TCP/IP14.1.2 IP地址14.1.3 端口14.1.4 HTTP/HTTPS 14.2 搭建自己的Web服务器14.3 urllib.request模块14.3.1 发送GET请求14.3.2 发送POST请求 14.4 JSON数据14.4.1 JSON文档的结构14.4.…

方案:TSINGSEE青犀智能分析网关皮带撕裂算法的场景应用

在工地矿山等现实场景中&#xff0c;皮带运输在生产过程中是必不可少的&#xff0c;然而&#xff0c;由于长时间高强度的运转&#xff0c;皮带很容易发生撕裂、破损、跑偏等问题。这些问题会严重影响生产速度&#xff0c;甚至会导致严重的安全事故。为了有效预防此类安全事故发…

短视频抖音账号矩阵系统源码开发者自研(四)

抖音是一款备受欢迎的短视频APP&#xff0c;拥有数亿的用户&#xff0c;其中包括了大量的粉丝。为了让更多的人能够发现和观看到你的视频&#xff0c;抖音SEO是必不可少的一环&#xff0c;特别是对于拥有企业或个人品牌的用户来说。在这个过程中&#xff0c;抖音SEO源码的开源部…