当前位置: 首页 > news >正文

数据结构与算法:滑动窗口

前言

滑动窗口一般主要用于解决子数组或子串问题,这类的题目更看重对题目的分析和转化。

一、原理

在整个数组上,用l和r分别控制窗口的左右边界,r++就扩大,l++就减小。

窗口的范围和题目中某个指标间存在单调关系时,就可以考虑使用滑动窗口解决,整个过程一般会需要用某种数据结构或算法来维护信息,每次统计的就是子数组以每个位置开头或结尾的答案。

二、题目

1.无重复字符的最长子串

class Solution {
public:int lengthOfLongestSubstring(string s) {map<int,int>mp;map<int,int>::iterator iter;int ans=0;for(int l=0,r=0;r<s.length();r++){iter=mp.find(s[r]);if(iter!=mp.end()){l=max(l,mp[s[r]]+1);}ans=max(ans,r-l+1); mp[s[r]]=r;}return ans;}
};

首先分析题目可以发现存在单调性:窗口越大,越可能出现重复字符。

之后,考虑重复这一性质,可以用一个哈希map来维护每个字符最晚出现的位置,这样当遇到重复的字符,直接让l跳到该字符上次出现位置的下一个位置即可。这里注意l只能往前跳!

2.长度最小的子数组

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int ans=INT_MAX;for(int l=0,r=0,sum=0;r<nums.size();r++){sum+=nums[r];while(sum-nums[l]>=target){sum-=nums[l];l++;}if(sum>=target){ans=min(ans,r-l+1);}}return ans==INT_MAX?0:ans;}
};

分析题目单调性:窗口越大,累加和越大

于是考虑使用前缀和来维护子数组累加和,因为要求长度最小,所以若窗口缩小时累加和仍大于target,就让窗口缩小。

3.最小覆盖子串

class Solution {
public:string minWindow(string s, string t) {map<int, int> cnts;for (int i = 0; i < t.length(); i++) // 统计“欠”{cnts[t[i]]--;}int len = INT_MAX;int start = 0;for (int l = 0, r = 0, debts = t.length(); r < s.length(); r++) {if (cnts[s[r]]++ < 0) // 仍然“欠”{debts--; // 需要“还”的减少}if (debts == 0) // t中所有字符均满足{while (cnts[s[l]] > 0) // 尽量短{cnts[s[l]]--;l++;}if (len > r - l + 1) {len = r - l + 1;start = l;}}}return len == INT_MAX ? "" : s.substr(start, len);//substr第二个参数是取几个!}
};

 这个题就非常需要思考了,这个思路确实很妙。

先分析单调性:窗口越大,子串越有可能符合包含t中每个字符的标准

整体的思路是,为了统计子串中是否都包含了t中字符,所以可以从“欠”和“还”的角度考虑。

具体来说,先统计t中出现的字符,将其出现次数设置为“欠”,即子串还“欠”这些字符才能满足标准。之后,设置debts变量,表示一共“欠”的字符个数,若新加入的字符是“欠”的状态,就让debts减1。当debts等于0时,即不再“欠”字符了,就开始“还”。具体是只要窗口左侧字符处于“盈余”状态,就缩窗口,让子串尽量小。为了返回一个串,所以每次长度更新时记一下当前窗口左侧位置。

注意substr函数的第二个参数是取几个字符!!!

4.加油站

class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int n=gas.size();// vector<int>rest;// for(int i=0;i<2;i++)// {//     for(int j=0;j<n;j++)//     {//         rest.push_back(gas[j]-cost[j]);//     }// }for(int l=0,r=0,sum;l<n;l=r+1,r=l){sum=0;//while(sum+rest[r]>=0)while(sum+gas[r%n]-cost[r%n]>=0){       if(r-l+1==n){return l;}   //sum+=rest[r];sum+=gas[r%n]-cost[r%n];r++;}}return -1;}
};

 这个题的难点在于可以转一圈,暴力的做法就是开个两倍长度数组,简便方法是取r%n位置的数。

这个题用滑动窗口主要是题目的情景比较符合。

所以依然要用前缀和来维护剩余油量,当剩余大于0时,就一直往外扩,直到长度为n,即可以走一圈时返回此时窗口左边界,即起始位置。

5.替换子串得到平衡字符串

class Solution {
public:int balancedString(string s) {int n=s.length();map<int,int>cnts;for(int i=0;i<n;i++){cnts[s[i]]++;}int debts=0;for(map<int,int>::iterator iter=cnts.begin();iter!=cnts.end();iter++){iter->second=iter->second<n/4?0:n/4-iter->second;debts-=iter->second;//“欠”}//转化成“找最小覆盖子串”int ans=INT_MAX;for(int l=0,r=0;r<n;r++){if(cnts[s[r]]++<0){debts--;}if(debts==0){while(cnts[s[l]]>0){cnts[s[l]]--;l++;}ans=min(ans,r-l+1);}}return ans;}
};

 下面几个题就逐渐开始抽象起来了,分析难度越来越大,更考验对题目的转化。

这个题需要一步转化,先统计一遍各个字符的出现次数,然后将不够的当作“欠”,转化成求这些字符的“最小覆盖子串”。

6.K 个不同整数的子数组

class Solution {
public:int subarraysWithKDistinct(vector<int>& nums, int k) {//转化 -> 小于等于k-小于等于k-1return f(nums,k)-f(nums,k-1);}int f(vector<int>&nums,int k){map<int,int>cnts;int ans=0;for(int l=0,r=0,kinds=0;r<nums.size();r++){if(++cnts[nums[r]]==1){kinds++;}while(kinds>k){if(--cnts[nums[l]]==0){kinds--;}l++;}ans+=r-l+1;//0~3:0~3+1~3+2~3+3~3->四种}return ans;}
};

 首先分析题目,会发现等于k其实没有单调性,那就考虑将找等于k的个数转化成大于等于k-大于等于k-1的个数。因为窗口越大,大于等于k的子数组越长,有单调性。

之后就是在统计出现次数时统计种类,当种类大于k时让窗口缩小。最后要注意,滑动窗口每次统计的是子数组在此范围上以右边界结尾的答案,所以种类数即窗口长度

7.至少有 K 个重复字符的最长子串

class Solution {
public:int longestSubstring(string s, int k) {int ans=0;for(int require=1;require<=26;require++){map<int,int>cnts;for(int l=0,r=0,kinds=0,satisfy=0;r<s.length();r++){cnts[s[r]]++;if(cnts[s[r]]==1){kinds++;}if(cnts[s[r]]==k){satisfy++;}while(kinds>require){if(cnts[s[l]]==1){kinds--;}if(cnts[s[l]]==k){satisfy--;}cnts[s[l]]--;l++;}if(satisfy==require){ans=max(ans,r-l+1);}}}return ans;}
};

这个题就更困难了,转化的这一步太难想了。

首先,需要分析出这个题里到底是谁存在单调性,分析可知,窗口越大时,字符种类越多。所以要找的子串即,只有1种字符大于等于k,只有2种字符大于等于k……只有26种字符大于等于k这些情况的最长子串的最大值。(太难想了这个思路)

所以枚举二十六个字母,每次都维护当前map里的字符种类和满足大于等于k这个条件的种类。当种类比需要的大时,让窗口缩小,最后当满足的种类等于需要的种类数时,统计答案。

总结

滑动窗口的这些题确实很难,得多见多想,将复杂问题转化成简单问题。

END

相关文章:

数据结构与算法:滑动窗口

前言 滑动窗口一般主要用于解决子数组或子串问题&#xff0c;这类的题目更看重对题目的分析和转化。 一、原理 在整个数组上&#xff0c;用l和r分别控制窗口的左右边界&#xff0c;r就扩大&#xff0c;l就减小。 当窗口的范围和题目中某个指标间存在单调关系时&#xff0c;…...

江协科技/江科大-51单片机入门教程——P[2-1] 点亮一个LED

本节将向大家介绍如何用 51 单片机去控制开发板上的 LED。开发板上的 LED 位置标注有 “LED 模块”。 第二章要写 3 个程序代码&#xff1a;第一个代码实现点亮开发板上的第一个 LED&#xff1b;第二个代码让第一个 LED 以 1 秒为周期闪烁&#xff1b;第三个代码使 8 个 LED 以…...

leetcode hot 100 41. 缺失的第一个正数

代码 测试用例 测试用例 测试结果 41. 缺失的第一个正数 已解答 困难 相关标签 相关企业 提示 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xf…...

UniApp 使用 u-loadmore 完整步骤

文章目录 一、前期准备1. 安装 uView - UI 二、使用 u-loadmore组件1. 创建页面2. 编写页面代码模板部分&#xff08;loadmore-demo.vue&#xff09;样式部分脚本部分 三、要点补充1. u-loadmore 状态说明2. 数据请求优化3. 性能优化4. 兼容性问题 在 UniApp 开发中&#xff0c…...

设置电脑一接通电源就主动开机

文章目录 1、进入BIOS2、设置4、功能弊端5、电脑自动开机的设置 1、进入BIOS 在电脑重启时&#xff0c;这时屏幕上会显示按XXX键到BIOS界面 没有进入BIOS提示的&#xff0c;按下面方法操作&#xff1a; 方法一 在开机显示logo的时候&#xff0c;立即按下面这几个按键&#xf…...

优艾智合机器人日本子公司成立,加速推进国际化布局

2月27日&#xff0c;工业移动机器人解决方案商优艾智合宣布日本子公司Youibot Robotics Japan株式会社&#xff08;以下简称“Youibot Japan”&#xff09;成立&#xff0c;并于东京举行开业典礼。此举标志着优艾智合在日本市场的现地服务能力进一步深化&#xff0c;是其全球化…...

自然语言处理NLP入门 -- 第七节预训练语言模型

1 什么是预训练模型&#xff1f; 在自然语言处理&#xff08;NLP&#xff09;里&#xff0c;训练一个好模型通常需要很多数据和计算资源。为了解决这个难题&#xff0c;就出现了“预训练模型”。 预训练模型 是指我们先在海量文本&#xff08;比如网络上爬到的大量文章、对话…...

Git GitHub基础

git是什么&#xff1f; Git是一个分布式版本控制系统&#xff0c;用于管理源代码的变更。它允许多个开发者在同一个项目上协作&#xff0c;同时跟踪每个修改的历史记录。 关键词&#xff1a; 分布式版本控制软件 软件 安装到我们电脑上的一个工具 版本控制 例如论文&…...

多平台文章同步工具PostSync 安装介绍

PostSync 是一个开源的用于多平台文章同步的工具 环境安装 安装 Python&#xff1a;PostSync 是基于 Python 开发的&#xff0c;你需要确保系统中已经安装了 Python 环境&#xff0c;建议使用 Python 3.7 及以上版本。你可以从 Python 官方网站 下载并安装适合你操作系统的版…...

PXE批量网络装机与Kickstart自动化安装工具

目录 一、系统装机的原理 1.1、系统装机方式 1.2、系统安装过程 二、PXE批量网络装机 2.1、PXE实现原理 2.2、搭建PXE实际案例 2.2.1、安装必要软件 2.2.2、搭建DHCP服务器 2.2.3、搭建TFTP服务器 2.2.4、挂载镜像并拷贝引导文件到tftp服务启动引导文件夹下 2.2.5、编…...

css的复合选择器

1.1什么是复合选择器 在css中&#xff0c;选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基本选择器进行组合形成。 复合选择器可以更准确、更高效的选择目标元素(标签)由两个或多个基础选择器&#xff0c;通过不同的方式组合…...

Wireshark Lua 插件教程

本⽂主要介绍 Lua 脚本在 Wireshark 中的应⽤, Lua 脚本可以在 Wireshark 中完成如下功能: 从⽹络包中提取数据, 或者统计⼀些数据包(Dumper) 需要解析⼀种 Wireshark 不提供原⽣⽀持的协议(Dissector) ⽰例 协议解析 VREP 协议是 NOGD 框架对于 TRIP 协议的⼀种延伸和扩展…...

mysql怎样优化where like ‘%字符串%‘这种模糊匹配的慢sql

一 问题描述 工作中经常遇到这种模糊匹配的慢sql&#xff1a; select * from 表名 where 字段 like %字符串%; 由于前面有%&#xff0c;导致无法走该字段上的索引。 二 解决办法 ① 给该字段创建一个全文索引 CREATE FULLTEXT INDEX 索引名 ON 表名 (字段名); ② 改写sq…...

Python代码片段-断点任务

使用Python处理一堆长耗时任务的时候&#xff0c;为了防止异常退出程序或者手动退出程序后丢失任务进度&#xff0c;可用使用断点的方式记录任务进度&#xff0c;下次重载任务后&#xff0c;继续运行上次未完成的任务即可。 这里用json文件作为数据持久化的方式&#xff0c;免…...

mapbox基础,使用geojson加载heatmap热力图层

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️heatmap热力图层样式二、🍀使用geojs…...

03.检测 Zabbix agent

TOC 利用 zabbix_get 工具测试 Zabbix Agent 是否正常 # 安装 zabbix-get [rootUbuntu2204 ~]#apt install -y zabbix-get# 使用zabbix_get 工具查看验证 agent 是否正常 返回1表示正常 [rootUbuntu2204 ~]#zabbix_get -s 10.0.0.110 -p 10050 -k "agent.ping"故障…...

Vue 3 + Vite 项目配置访问地址到服务器某个文件夹的解决方案

前言 在开发 Vue 3 Vite 项目时&#xff0c;我们经常需要将项目部署到服务器的某个特定文件夹下。例如&#xff0c;将项目部署到 /my-folder/ 目录下&#xff0c;而不是服务器的根目录。这时&#xff0c;我们需要对 Vite 和 Vue Router 进行一些配置&#xff0c;以确保项目能…...

JavaScript将:;隔开的字符串转换为json格式。使用正则表达式匹配键值对,并构建对象。多用于解析cssText为style Object对象

// 使用正则表达式匹配键值对&#xff0c;并构建对象 let string2Json(s)>{const r {};s.replace(/&#xff1b;/g, ;).replace(/\;/g, \n).replace(/&#xff1a;/g, :).replace(/\n/g, \n)//合并多个换行符.split(\n).forEach(item > {const [k, v] item.split(:);(k…...

MT-Metrics

MT-Metrics 是一类用于评估生成文本质量的指标&#xff0c;最初用于机器翻译任务&#xff0c;后来扩展到生成任务&#xff08;如对话生成、文本摘要等&#xff09;。它的核心思想是通过比较生成文本与参考文本之间的相似性&#xff08;如词汇重叠、句法结构、语义相似性&#x…...

【数据结构第十六节】实现链式结构二叉树(详细递归图解—呕心沥血版!)

必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。云边有个稻草人-CSDN博客 这节课挺抽象&#xff08;苦笑&#xff09;&#xff0c;没事&#xff0c;我会帮你&#xff01;干就完了&#xff01; &#xff08;目录在路上&#xff09; 正文开始—— 引言 用链表…...

CLAP-htsat-fused部署指南:Docker资源限制与OOM Killer规避策略

CLAP-htsat-fused部署指南&#xff1a;Docker资源限制与OOM Killer规避策略 1. 项目概述 CLAP-htsat-fused是一个基于LAION CLAP模型的零样本音频分类Web服务。这个工具能够对任意音频文件进行语义分类&#xff0c;无需预先训练特定类别的模型。无论是狗叫声、猫叫声、鸟叫声…...

【PyCon 2025闭门分享精要】:Python 3.14 JIT底层调度器深度调优——用3行代码撬动47% CPU利用率提升

第一章&#xff1a;Python 3.14 JIT编译器性能调优配置总览Python 3.14 引入了实验性内置 JIT&#xff08;Just-In-Time&#xff09;编译器&#xff0c;基于 Pyston 的优化后端重构&#xff0c;支持函数级动态编译与类型特化。该 JIT 默认处于禁用状态&#xff0c;需通过环境变…...

09 华夏之光永存:带领华为盘古大模型走向世界巅峰

09 华夏之光永存&#xff1a;带领华为盘古大模型走向世界巅峰 小标题&#xff1a;鸿蒙生态深度协同&#xff1a;端侧大模型原生融合方案 文章摘要 本文作为系列专栏第九篇&#xff0c;聚焦华为盘古大模型与鸿蒙生态端侧原生适配、端边云全域协同核心痛点&#xff0c;针对当前端…...

打字不如说话,说话不如截图——AI 代码助手的多模态输入实践偈

整体排查思路 我们的目标是验证以下三个环节是否正常&#xff1a; 登录成功时&#xff1a;服务器是否正确生成了Session并返回了包含正确 JSESSIONID的Cookie给浏览器。 浏览器端&#xff1a;浏览器是否成功接收并存储了该Cookie。 后续请求&#xff1a;浏览器在执行查询等操作…...

为什么你的C# 13主构造函数反而变慢了?揭秘字段初始化顺序、属性注入与依赖解析的致命时序冲突

第一章&#xff1a;为什么你的C# 13主构造函数反而变慢了&#xff1f;C# 13 引入的主构造函数&#xff08;Primary Constructors&#xff09;本意是简化类型初始化语法&#xff0c;但实际性能表现可能与直觉相悖——在某些场景下&#xff0c;它反而比传统构造函数更慢。根本原因…...

双目视觉实战:如何用OpenCV和Python实现简易3D建模(附完整代码)

双目视觉实战&#xff1a;如何用OpenCV和Python实现简易3D建模&#xff08;附完整代码&#xff09; 当你第一次看到3D电影中跃然眼前的画面&#xff0c;或是用手机扫描物体生成三维模型时&#xff0c;是否好奇过这背后的技术原理&#xff1f;双目视觉技术正是实现这些酷炫效果的…...

用Python搞定拉普拉斯变换:从电路分析到微分方程实战(附完整代码)

用Python搞定拉普拉斯变换&#xff1a;从电路分析到微分方程实战&#xff08;附完整代码&#xff09; 在工程实践中&#xff0c;拉普拉斯变换就像一把瑞士军刀&#xff0c;能将复杂的微分方程瞬间转化为可解的代数问题。想象一下&#xff0c;当你面对一个包含电阻、电感和电容…...

MTK平台Camera移植避坑指南:从驱动添加到DWS配置的完整流程(基于Kernel 4.19)

MTK平台Camera移植避坑指南&#xff1a;从驱动添加到DWS配置的完整流程&#xff08;基于Kernel 4.19&#xff09; 在嵌入式设备开发中&#xff0c;Camera模块的移植往往是系统集成中最具挑战性的环节之一。特别是基于MTK平台的Android设备&#xff0c;Camera驱动的移植涉及从内…...

告别 GCC 11 兼容性烦恼:在 Ubuntu 22.04 上为旧内核项目配置专用编译环境(gcc-9 实战)

在 Ubuntu 22.04 上构建多版本 GCC 编译环境的完整指南 当现代 Linux 发行版遇上历史悠久的开源项目&#xff0c;版本兼容性问题往往成为开发者最大的痛点。Ubuntu 22.04 默认搭载的 GCC 11 编译器虽然性能优异&#xff0c;但在编译某些旧版内核或系统级软件时&#xff0c;可能…...

AI时代的算法思维:大经典排序学习矩

引言 在现代软件开发中&#xff0c;性能始终是衡量应用质量的重要指标之一。无论是企业级应用、云服务还是桌面程序&#xff0c;性能优化都能显著提升用户体验、降低基础设施成本并增强系统的可扩展性。对于使用 C# 开发的应用程序而言&#xff0c;性能优化涉及多个层面&#x…...