滑动窗口大总结!!!妈妈以后再也不担心我不会做滑动窗口啦~
写在前面:全部题都源于力扣
- 讲解
- 题目一:最小覆盖子串
- 题目二:字符串排列
- 题目三:找所有字母异位词
- 题目四:无重复字符的最长子串
- 题目五:滑动窗口的最大值
讲解
滑动窗口算法技巧主要用来解决子数组问题,比如让你寻找符合某个条件的最长/最短子数组。
如果用暴力解的话,你需要嵌套 for 循环这样穷举所有子数组,时间复杂度是O(n2)
for(int i = 0; i < nums.size(); i ++){for(int j = i; j < nums.size(); j ++){//维护一个nums[i,j]的子数组}
}
滑动窗口其实也并不难就是维护一个窗口,不断滑动,不断更新答案,大致逻辑:
int left = 0, right = 0;while (right < nums.size()) {// 增大窗口window.addLast(nums[right]);right++;while (window needs shrink) {// 缩小窗口window.removeFirst(nums[left]);left++;}
}
由于这套逻辑left和right都不会回退,所以滑动窗口的时间复杂度是O(n)
没了,讲解到此结束
只学讲解是没有办法乱杀滴,接下来就靠着这个模板魔改解决hard题吧!
题目一:最小覆盖子串
力扣难度hard->传送门
滑动窗口的思路是这样的:
- 在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。
问:为什么设计为左闭右开区间?
答:因为这样初始化 left = right = 0 时区间 [0, 0) 中没有元素,但只要让 right 向右移动(扩大)一位,区间 [0, 1) 就包含一个元素 0 了。如果设置为两端都开的区间,那么让 right 向右移动一位后开区间 (0,1) 仍然没有元素;如果设置为两端都闭的区间,那么初始区间 [0, 0] 就包含了一个元素。这两种情况都会给边界处理带来不必要的麻烦。
- 先不断地增加 right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求(包含了 t 中的所有字符)。
- 此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求(不包含 t 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果
- 重复第 2 和第 3 步,直到 right 到达字符串 s 的尽头。
talk is cheap,show me the code!
首先,需要window和need两个哈希表,用来记录窗口中已有的字符和需要凑齐的字符
// 记录 window 中的字符出现次数
unordered_map<char, int> window;
// 记录所需的字符出现次数
unordered_map<char, int> need;
for(char c : t) need[c] ++;
现在开始套模板,只需要思考以下几个问题:
- 什么时候应该移动 right 扩大窗口?窗口加入字符时,应该更新哪些数据?
答:只要窗口内没有满足t字符都有的话就应该继续扩大窗口,窗口加入字符时,需要更新窗口大小(window++),必备字符个数(window[c]++),已满条件的字符数(valid++)。
while(right < s.size()){char c = s[right];//加入滑动窗口的值right ++;//窗口变大//新加入的值是否需要,需要的话:if(need(c)){window[c]++;//已有的必备值加加if(window[c] == need[c]) valid++;//如果某个字符在此窗口已经满足条件,valid++}
}
- 什么时候窗口应该暂停扩大,开始移动 left 缩小窗口?从窗口移出字符时,应该更新哪些数据?
答:当 valid 满足 need 时应该收缩窗口,应该在收紧窗口的时候更新最终数据,更新窗口大小,更新valid(移除元素了,这里只可能减),window[字符]数量,另外更新最小覆盖子串的起始位置。因为答案一定是在缩窗口的时候出现的,所以应该在这里更新len和start
// 判断左侧窗口是否要收缩while (valid == need.size()) {// 在这里更新最小覆盖子串if (right - left < len) {start = left;len = right - left;}// d 是将移出窗口的字符char d = s[left];// 缩小窗口left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d])valid--;window[d]--;}
- 我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?
答:一定是在缩小的时候
整体代码:
class Solution {
public:string minWindow(string s, string t) {unordered_map<char, int> need, window;for (char c : t) {need[c]++;}int left = 0, right = 0;// 记录window中的字符满足need条件的字符个数int valid = 0;// 记录最小覆盖子串的起始索引及长度int start = 0, len = INT_MAX;while (right < s.size()) {// c 是将移入窗口的字符char c = s[right];// 扩大窗口right++;// 进行窗口内数据的一系列更新if (need.count(c)) {window[c]++;if (window[c] == need[c])valid++;}// 判断左侧窗口是否要收缩// 用while!!一种缩到不能再缩while (valid == need.size()) {// 在这里更新最小覆盖子串// 必须先将len记录下来再更新窗口大小// 只有这样才能记录每一次合法len,然后更新if (right - left < len) {start = left;len = right - left;}// d 是将移出窗口的字符char d = s[left];// 缩小窗口left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d])valid--;window[d]--;}}}// 返回最小覆盖子串// 等于INT_MAX的话返回的是""不是" "return len == INT_MAX ? "" : s.substr(start, len);}
};
题目二:字符串排列
力扣567
mid难度,s1是可以包含重复字符的哦
是明显的滑动窗口算法,相当给你一个 S 和一个 T,请问你 S 中是否存在一个子串,包含 T 中所有字符且不包含其他字符?
基本和题目一是一样的,只有几个地方需要注意:
- 本题移动 left 缩小窗口的时机是窗口大小大于 t.length() 时,因为排列嘛,显然长度应该是一样的。
- 当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列,所以立即返回 true。至于如何处理窗口的扩大和缩小,和最小覆盖子串完全相同。
完整代码:
class Solution {
public:bool checkInclusion(string t, string s) {unordered_map<char, int> need, window;for (char c : t) need[c]++;int left = 0, right = 0, valid = 0;while (right < s.size()) {char c = s[right++];if (need.count(c)) {window[c]++;if (window[c] == need[c]) valid++;}while (right - left > t.size()) { // 严格大于,以便准确控制窗口大小char d = s[left++];if (need.count(d)) {if (window[d] == need[d]) valid--;window[d]--;}}// valid == need.size()!!!if (right - left == t.size() && valid == need.size()) // 确保窗口大小严格等于t的长度return true;}return false;}
};
题目三:找所有字母异位词
力扣438
异位词,就是排列啊,搞了个高端的说法,也糊弄不了我们这些绝顶聪明的娃
直接上代码
class Solution {
public:vector<int> findAnagrams(string s, string t) {unordered_map<char, int> need, window;for (char c : t) {need[c]++;}int left = 0, right = 0;int valid = 0;// 记录结果vector<int> res;while (right < s.size()) {char c = s[right];right++;// 进行窗口内数据的一系列更新if (need.count(c)) {window[c]++;if (window[c] == need[c]) {valid++;}}// 判断左侧窗口是否要收缩while (right - left > t.size()) {char d = s[left];left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d]) {valid--;}window[d]--;}}if(right - left == t.size() && valid == need.size()){res.push_back(left);}}return res;}
};
题目四:无重复字符的最长子串
力扣3.
这题在双指针里面用双指针解决过了
如果用滑动窗口的话也很容易
窗口缩的条件就是window[c] > 1,说明有重复了
和双指针思路一模一样
双指针:维护[j,i]数组
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int s[N];
int main(){int n;int r = 0;cin >> n;for(int i = 0, j = 0; i < n; i ++){cin >> a[i];s[a[i]] ++;//记录个数while(s[a[i]] > 1){-- s[a[j ++]];}r = max(r, i - j + 1);}cout << r;
}
滑动窗口:
class Solution {
public:int lengthOfLongestSubstring(string s) {int left = 0;int right = 0;int r = 0;unordered_map<char, int> window;while(right < s.size()){char c = s[right];right++;window[c]++;while(window[c] > 1){char d = s[left];window[d]--;left++;}r = max(r, right - left);}return r;}
};
题目五:滑动窗口的最大值
经典滑动窗口,力扣239
又名单调队列的实现
和题目1~4不一样,这题滑动窗口大小固定,每一次的移动也固定,难点在于求最大值
这里实现队列,要有pop,push操作,因为题目的特殊性,再加个返回最大值的max操作
我们需要逐步实现这三个API
push:
push 方法依然在队尾添加元素,但是要把前面比自己小的元素都删掉
void push(int n) {// 将前面小于自己的元素都删除while (!maxq.empty() && maxq.back() < n) {maxq.pop_back();}maxq.push_back(n);
}
max:
如果每个元素被加入时都这样操作,最终单调队列中的元素大小就会保持一个单调递减的顺序,因此我们的 max 方法可以可以这样写:
int max() {// 队头的元素肯定是最大的return maxq.front();}
pop:
pop 方法在队头删除元素 n:
void pop(int n) {if (n == maxq.front()) {maxq.pop_front();}
}
所以综合代码:
class slidingQueue{
private:deque<int> maxq;
public:void push(int n){while(!maxq.empty() && n > maxq.back()) maxq.pop_back();maxq.push_back(n);}int max(){return maxq.front();}void pop(int n){if(!maxq.empty() && maxq.front() == n) maxq.pop_front();}
};
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {slidingQueue window;vector<int> res;for(int i = 0; i < nums.size(); i ++){if(i < k - 1){window.push(nums[i]);}else{window.push(nums[i]);res.push_back(window.max());window.pop(nums[i - k + 1]);}}return res;}
};
相关文章:
滑动窗口大总结!!!妈妈以后再也不担心我不会做滑动窗口啦~
写在前面:全部题都源于力扣 讲解题目一:最小覆盖子串题目二:字符串排列题目三:找所有字母异位词题目四:无重复字符的最长子串题目五:滑动窗口的最大值 讲解 滑动窗口算法技巧主要用来解决子数组问题&#…...
从地铁客流讲开来:客流统计与清分释义
一、常见的客流统计 1. 进站客流 定义:指在某个时间段内,乘客进入地铁站的数量。示例:如果某天早上8点到9点之间有5000人次进入地铁站,则这段时间内的进站客流为5000人次。 2. 出站客流 定义:指在某个时间段内&…...
《Excelize权威指南》新书发布
在数据洪流涌动的数字化时代,数据处理与分析已跃升为解锁无限洞察力的金钥匙,赋能商业智慧、重塑医疗健康版图、驱动教育科研创新。然而,当数据量级爆炸式增长,传统工具如 Excel 虽被誉为数据处理领域的常青树,其手动操…...
Go语言加Vue3零基础入门全栈班11 Go语言+gorm用户管理系统实战 2024年08月03日 课程笔记
概述 如果您没有Golang的基础,应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728Go语言操作MySQL开发用户管理系统API教程_20240729Redis零基础快速入门_20231227GoRedis开发用户管理系统API实战_20240730Mo…...
【设计模式】代理模式详解
1.简介 代理模式是常用的Java设计模式,该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类,并在事后处理消息等。代理类与委托类之间通常存在关联关系,一个代理类对象与一个委托类对象关…...
Python变量和简单的数据类型
1、变量 massageHello python world! print(massage) massageHello world print(massage) 运行这个代码发现,同一个变量出现两个不同的结果 Hello python world! Hello world 在程序中,可随时修改变量的值&…...
切比雪夫距离
切比雪夫距离(Chebyshev Distance),又称棋盘距离或最大值距离,是一种用于测量两个点之间距离的度量方法。在二维平面上,切比雪夫距离定义为两个点之间的最大坐标差值。其公式如下: DChebyshevmax(∣x2−…...
计算机基础(Windows 10+Office 2016)教程 —— 第4章 计算机网络与Internet(下)
第4章 计算机网络与Internet 4.4 局域网4.4.1 局域网概述4.4.2 以太网4.4.3 令牌环网4.4.4 无线局域网 4.5 Internet4.5.1 Internet 概述4.5.2 Internet 的基本概念4.5.3 Internet 的接入4.5.4 万维网 4.6 Internet的应用4.6.1 电子邮件4.6.2 文件传输4.6.3 搜索引擎 4.4 局域网…...
机器学习用Python还是R?哪个更好一些?
选择使用Python还是R来进行机器学习取决于多个因素,包括个人偏好、项目需求以及可用的资源。这里我可以简要比较一下它们的优缺点: Python的优势: 通用性和灵活性: Python是一种通用编程语言,可以用于多种用途&#…...
4个自定义倒计时
<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>4个自定义倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;user-select: none;body {background: #0b1b2c;}}hea…...
linux系统编程中Shell脚本配置,及linux脚本中的man test
Shell脚本配置是指在脚本中设置各种参数、选项和环境,以确保脚本能够根据预期的需求和环境执行。配置可以包括变量设置、环境变量、命令选项和错误处理等。 1. 脚本开头的配置 Shebang 第一行通常是shebang,它告诉系统使用哪个解释器来执行脚本。例如…...
Win7虚拟机分享(已安装VMware Tools)
前言 之前写过VMware安装Win7并安装VMware tools的博客,但操作仍显繁琐。后来发现可以直接分享已经配置好的虚拟机,所有软件都是安装好的,解压即用。 一. VMware Win7虚拟机配置 已完成的配置和安装的软件 专业版Win7系统(已永久激活)VMware…...
CANOpen EMCY紧急报文介绍
什么是CANOpen紧急报文 CANOpen中的Emcy紧急报文用于当设备出现故障或警告时,向其它节点报告故障或警告使用的。如设备某个设备出现过压或过流时,就可以发送紧急报文。 紧急报文的格式 错误代码:是0x1003索引预定义错误字段的内容ÿ…...
JAVA项目
目录 一、前言 二、技术介绍 三、项目实现流程 四、论文流程参考 五、核心代码截图 专注于大学生实战开发、讲解和毕业答疑等辅导,获取源码后台 一、前言 在数字化音乐时代,个性化推荐已成为提升用户体验、促进音乐消费的重要手段。为此࿰…...
️ LangChain +Streamlit+ Llama :将对话式人工智能引入您的本地设备(下篇)
引言:种下一棵树最好的时间是十年前,其次是现在 书接上回:将对话式人工智能引入您的本地设备成为可能CSDNhttps://mp.csdn.net/mp_blog/creation/editor/140865426 目的:在这个大模型横行的时候,我们常用电脑如何开展大模型的工作…...
Kafka实战(Scala操作)
Kafka基础讲解部分 Kafka基础讲解部分 Kafka实战(Scala操作) 1、引入依赖 版本: <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.report…...
Android Framework 之WMS详解
1.WMS说的就是 WindowManagerService:负责为Activity对应的窗口分配Surface,管理Surface的显示顺序以及位置尺寸,控制窗口动画 。 它是Android系统中为各个客户端即每个app来提供这样的服务的一个类。 在Android系统中在systemServer 进程和各…...
opencv-图像仿射变换
仿射变换设计图像位置角度的变化,是深度学习预处理中常用的功能。仿射变换就是对图像的平移缩放旋转翻转操作的组合 如下图,对图中点1,2,3与图二中三个点一一映射,仍然形成三角形,但形状已经发生改变,通过这两组三点求…...
算法的基本概念
一、算法的基本概念思维导图 二、什么是算法: 1.我们知道数据结构就是将我门现实的世界中的问题数据化,存入计算机中,并实现对数据结构的一些基本操作。 2.算法就是如何处理这些存入计算机中的信息,以求高效的解决实际问题。 3…...
124. Go Template应用实例:用代码生成代码
文章目录 生成器模式生成器代码生成 本文用生成器模式作为例子,来演示如何用代码生成代码。 生成器模式 熟悉 Java 开发的同学都知道,lombok 有一个著名的注解 Builder ,只要加在类上面,就可以自动生成 Builder 模式的代码。如下…...
【AI实践】阿里云方言文本转语音TTS
最近要做一些普通话和方言demo 找一个免费工具 免费在线文字转语音工具 | edge-tts 在线体验 (bingal.com) 还有一些方言在阿里云上找了下,基于官方demo改了一下 阿里云语音合成接口说明_智能语音交互(ISI)-阿里云帮助中心 (aliyun.com) 如何下载安装、使用语音…...
java 之 各类日期格式转换
一、前言 大家在开发过程中必不可少得和日期打交道,对接别的系统时,时间日期格式不一致,每次都要转换! 从 Java1 到 Java8 将近 20 年,再加上 Java8 的普及时间、各种历史 API 兼容过渡时间。我们很多时候需要在旧时间 API 与新时…...
Nvidia黄仁勋对话Meta扎克伯格:AI和下一代计算平台的未来 | SIGGRAPH 2024对谈回顾
在今年的SIGGRAPH图形大会上,Nvidia创始人兼CEO黄仁勋与Meta创始人马克扎克伯格进行了一场长达60分钟的对谈。这场对话不仅讨论了AI的未来发展和Meta的开源哲学,还发布了不少新产品,并深入探讨了下一代计算平台的可能性。 引言 人工智能的发…...
【JAVA设计模式】适配器模式——类适配器模式详解与案例分析
前言 在软件设计中,适配器模式(Adapter Pattern)是一种结构型设计模式,旨在使不兼容的接口能够协同工作。它通过引入一个适配器类,帮助两个接口之间进行适配,使得它们能够互相操作。本文将详细介绍适配器模…...
【Vue】全局组件和局部组件
一、全局组件 定义: 全局组件是在整个Vue应用中都可以使用的组件。它们被注册在Vue的根实例上,因此可以在任何子组件的模板中被引用,而无需在每个组件中重复注册。 注册方式: 全局组件通过Vue.component方法进行注册。这个方法接…...
react引入高德地图并初始化卫星地图
react引入高德地图并初始化卫星地图 1.安装依赖 yarn add react-amap amap/amap-jsapi-loader2.初始化地图 import AMapLoader from "amap/amap-jsapi-loader"; import { FC, useEffect, useRef, useState } from "react";const HomeRight () > {con…...
2024最简七步完成 将本地项目提交到github仓库方法
2024最简七步完成 将本地项目提交到github仓库方法 文章目录 2024最简七步完成 将本地项目提交到github仓库方法一、前言二、具体步骤1、github仓库创建2、将远程仓库拉取并合并(1)初始化本地仓库(2)本地仓库与Github仓库关联&…...
前端WebSocket入门,看这篇就够啦!!
在HTML5 的早期开发过程中,由于意识到现有的 HTTP 协议在实时通信方面的不足,开发者开始探索能够在 Web 环境下实现双向实时通信的新的通信协议,提出了 WebSocket 协议的概念。 一、什么是 WebSocket? WebSocket 是一种在单个 T…...
漏洞复现-F6-11泛微-E-Cology-SQL
本文来自无问社区,更多漏洞信息可前往查看http://www.wwlib.cn/index.php/artread/artid/15575.html 0x01 产品简介 泛微协同管理应用平台e-cology是一套企业级大型协同管理平台 0x02 漏洞概述 该漏洞是由于泛微e-cology未对用户的输入进行有效的过滤࿰…...
Turbo Boost 禁用
最近在做OAI NR的时候关闭CPU 睿频的时候出了一些问题,这里我把我找到的资料记录一下: 禁用 Turbo Boost 的过程可能会因不同的 BIOS/UEFI 和操作系统设置而有所不同。以下是一些可能的原因及解决方法: 可能的原因 BIOS/UEFI 设置问题: 你的…...
六安城南疫情最新消息/seo站内优化培训
如何下载配置Tomcat 去https://tomcat.apache.org/,下载要安装的Tomcat版本 选择next I Agree 这里的勾选是都是默认,我们可以不作选择,直接next 8080是端口号,默认8080,选择next 选择JDK的安装位置,如果…...
电商网站怎么做/搜索引擎优化技术有哪些
本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 最近公司要引入API测试工具,经过调查和了解,最终决定在SoapUI 和 Postman两种工…...
嘉兴丝绸大厦做网站的公司/手机导航下载2022新版
在Android中,项目目录下的res\drawable用来放置该项目的图片资源。 Android中提供了Bitmap类来获取图像文件信息,进行图像的平移、旋转及缩放等操作,并可以指定格式保存图像文件。 1.图像绘制 在绘制图像之前,需要从项目目录下的r…...
做网站可以赚钱么/网站排名搜索
很多小伙伴还是不太清楚海德能的哪个国家的。今天水天蓝环保简单介绍海德能是哪个国家的。首先弄清楚海德能属于哪个国家的?海德能属于:美国海德能,1963年在美国加利福尼亚洲市创立。并于1987年并入日本日东电工集团。日本电工背景࿱…...
百度快照和做网站有关系吗/谷歌seo营销
打开网页浏览器提示:此网页包含重定向循环的解决打开网页浏览器提示:此网页包含重定向循环的解决 网页生成了过多的重定向。清除此网站的 Cookie 或允许第三方 Cookie 可能会解决该问题。如果不能解决,则可能是服务器配置的问题,而不是您的计算机有问题。…...
做网站的网页图片素材怎么找/北京seo公司公司
本节主要对 Apollo 服务端设计原理进行解析。 配置发布后的实时推送设计 配置中心最重要的一个特性就是实时推送,正因为有这个特性,我们才可以依赖配置中心做很多事情。如图 1 所示。 图 1 简要描述了配置发布的大致过程。 用户在 Portal 中进行配置的…...