假期算法提升(带你彻底掌握滑动窗口)

news2024/11/15 22:23:52

呀哈喽,我是结衣。
今天我们要学习的是一种新的算法,也是一种双指针。不过他拥有一个全新的名字”滑动窗口“。

文章目录

  • 1.长度最小的子数组(medium)
    • 思路
    • 解题方法
    • Code
  • 2.无重复字符的最长子串(medium)
    • 思路
    • 解题方法
    • Code
  • 3.最大连续1的个数 III(medium)
    • 思路
    • 解题方法
    • Code
  • 4.将x减到0的最小操作数(medium)
    • 思路
    • 解题方法
    • Code
  • 5.水果成篮(medium)
    • 思路
    • 解题方法
    • Code
  • 6.找到字符串中所有字母异位词(medium)
    • 思路
    • 解题方法
      • 方法一(直接比较哈希表)
      • Code
      • 方法二(更优)
      • Code
  • 7.串联所有单词的子串(hard)
    • 思路
    • 解题方法
    • Code
  • 8.最小覆盖子串(hard)
    • 思路
    • 解题方法
    • Code
  • 总结规律

1.长度最小的子数组(medium)

题目链接:长度最小的子数组
题目描述
长度最小的子数组

思路

因为数组元素全为正整数,使得数组具有了一种单调性。以示例1为例子,2+3+1+2会大于7,那么我们好有必要继续向后遍历吗?没有必要,所以我们就会想到去掉前面的一个数变成3+1+2这样也就小于7啦,于是我们就继续遍历数组,3+1+2+4又会大于7了我就重复刚刚的操作,我们遍历完数组。

解题方法

又上面的思路我们可以想到定义两个指针left和right,right来遍历数组,left在后面跟着,是双指针不过有了新的名字“滑动窗口”
在这里插入图片描述
在图中我可以看到right和left共同维护着一段区间,而且right和left又可以移动,顾名思义就是滑动窗口
滑动窗口最重要的操作就是进窗口(移动right)判断 出窗口(移动left) 更新结果
只要这几步就可以做出题目来。
要注意的数更新结果的位置是不确定的,要根据具体的题目要求。

Code

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0,n = nums.size();
        int len = INT_MAX;
        for(int left = 0,right = 0;right<n;right++)//right++为进窗口
        {
            sum+=nums[right];
            while(sum>=target)//判断
            {
                len = min(len,right-left+1);//更新结果
                sum-=nums[left];
                left++;//出窗口
            }
        }
        return len==INT_MAX?0:len;
    }
};

2.无重复字符的最长子串(medium)

题目链接:无重复字符的最长子串
题目描述
在这里插入图片描述

思路

当我们不知道怎么写之前,我们可以运用暴力的方法先考虑问题。遍历字符串时(示例1),当我们遍历到第二个a时我们此时就出现了重复的字符,我们可以继续遍历,但那就没有必要了。我们可以重新遍历从第二个字符开始,再向后举行遍历,这样的化时间复杂度肯定就是O(N^2)了这就是暴力,但是我不要忘了应该怎么判断重复字符,看肯定看的出来,可是对于计算机我们就要记录一下字符了,所以我们就要运用到哈希表了。

解题方法

对于思路中的暴力解法,我们是可以做到很多优化的,运用“滑动窗口”,使得时间复杂度变为O(N)。
在这里插入图片描述
我们利用“滑动窗口”,来保证窗口中的元素一定不会重复就可以了,为了实现这个操作我们需要定义应该哈希表。因为这里的数据全是字符,所以我们可以用int数组来映射就可以了。
然后就是滑动窗口的经典操作:
滑动窗口最重要的操作就是进窗口(移动right)判断 出窗口(移动left) 更新结果

Code

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        int len = 0;
        int hash[128] = {0};
        for(int left = 0,right = 0;right<n;++right)//进窗口
        {
            hash[s[right]]++;
            while(hash[s[right]]>1)
            {
                hash[s[left]]--;
                left++;//出窗口
            }
            len = max(len,right-left+1);//更新结果
        }
        return len;
    }
};

3.最大连续1的个数 III(medium)

题目链接:最大连续1的个数 III
题目描述
在这里插入图片描述

思路

如果我们只是寻找最长的子字符串1,要考虑的东西就太多了。为了让这个题目更加的简单,我们应该采用一种正难则反的思想,我们把问题转化为寻找不超过k个0的最长子串。这样做就简单的多了,所以在写编程题的时候如果一点想法都没有不妨换个角度来想问题。

解题方法

当我们把问题简化之后,滑动窗口的想法就呼之欲出了,和前面一样,我们要做的就是维护窗口里的元素。这次呢,我们要维护的就是窗口内0元素不能超过k个。在此基础上我们再运用滑动窗口的公式就可以解决问题了:进窗口(移动right)判断 出窗口(移动left) 更新结果

Code

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        //正难则反,我们把问题转化为寻找不超过k个0的最长子串。
        int zero = 0,len = -1;
        for(int left = 0,right = 0;right<nums.size();++right)//进窗口
        {
            if(nums[right]==0) zero++;
            while(zero>k)//条件判断
            {
                if(nums[left]==0) zero--;
                left++;//出窗口
            }
            if(zero == k)len = max(len,right-left+1);//更新结果
        }
        return len==-1?nums.size():len;
    }
};

4.将x减到0的最小操作数(medium)

题目链接:将x减到0的最小操作数
题目描述
在这里插入图片描述

思路

同样的,直接写太麻烦了,要考虑的东西太过。还是正难则反,把问题转化一下不就变成了求目标值为sum-x最长子数组和,和第一题不就差不多了吗?

解题方法

和第一题类似,运用滑动窗口解决,不过再滑动窗口操作之前我们要判断一下sum-x是否为负数如果为负数的话,后面代码就会溢出的。了解完成后还是那四步:进窗口(移动right)判断 出窗口(移动left) 更新结果

Code

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        //正难则反,表面上让你找让x减小到0的最小操作数,实际是可以转化为求目标值为sum-x最长子数组和。
        int sum = 0;
        for(auto num:nums) sum+=num;
        int tar = sum-x;
        if(tar<0)
        return -1;
        int sum_ = 0,res = -1;
        for(int left = 0,right = 0;right<nums.size();++right)//进窗口
        {
            sum_+=nums[right];
            while(sum_>tar)//条件判断
            {
                sum_-=nums[left++];//出窗口
            }
            if(sum_==tar) res = max(res,right-left+1);//结果更新
        }
        return res == -1?res: nums.size()-res;
    }
};

5.水果成篮(medium)

题目链接:水果成篮
题目描述
在这里插入图片描述

思路

这道题的题目有点长,把他翻译翻译就是求最长不超过两类树的子数组。了解的这个,这道题就变得简单了。至于如何判断是否超过了两类树,又要用到哈希表了。

解题方法

写题的关将就在于理解题目,我们要把题目转化成我们熟悉的题目。就像这道题,转化后就自然而然地想到了我们滑动窗口。不过就是再加上一个哈希表。我们建立一个哈希数组,在建立一个kinds来判断窗口内树地种类。如何判断呢?
在这里插入图片描述
在这里插入图片描述

Code

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        //题目可能有点难懂,翻译一下就求最长不超过两类树的子数组。
        int hash[100001] = {0};
        int kinds = 0,n = fruits.size(),len = 0;
        for(int left = 0,right = 0;right<n;++right)//进窗口
        {
            if(hash[fruits[right]]++==0) kinds++;
            while(kinds>2)//条件判断
            {
                if(--hash[fruits[left]]==0) kinds--;
                left++;//出窗口
            }
            len = max(len,right-left+1);//更新结果
        }
        return len;
    }
};

6.找到字符串中所有字母异位词(medium)

题目链接:找到字符串中所有字母异位词
题目描述
在这里插入图片描述

思路

刚上手可能没想法,那就尝试暴力解法吧,依次遍历字符串以示例1为例。
在这里插入图片描述
暴力加哈希可以解决但时间复杂度太高。

解题方法

优化上面的暴力方法就需要滑动窗口了。不过这里我们着重要讲的是如何利用哈希表来出来这种情况。我们定义了两个哈希表。一个哈希1用来存放p字符串中的字符。哈希表2就用来存放s字符串里的字符。我们该如何判断窗口内的元素都是p中的呢?

方法一(直接比较哈希表)

因为字符串都是小写字母,每次我们窗口里有p.size()的字符时我们就比较两个哈希表,虽然每次比较都要遍历哈希表,但是小写字母只有26个时间复杂度度也就O(26*n)也是常数级别的可以忽略。

Code

class Solution {
public:
bool check(vector<int>hash,vector<int>hash_1)
{
    for(int i = 0;i<26;++i)
    {
        if(hash[i]!=hash_1[i]) return false;
    }
    return true;
}
    vector<int> findAnagrams(string s, string p) {
        int n_p = p.size();
        vector<int> hash_1(26,0);
        for(auto tmp:p) hash_1[tmp-'a']++;
        int n = s.size();
        int left = 0,right = 0;
        vector<int>hash(26,0);
        vector<int>res;
        while(right<n)
        {
            hash[s[right]-'a']++;
            if(right>=n_p-1)
            {
                if(check(hash,hash_1))
                res.push_back(left);
                hash[s[left]-'a']--;
                left++;
            }
            right++;
        }
        return res;
    }
};

方法二(更优)

我们利用一个count来记录窗口内p中字符串的个数。是p中的字符count就++,后续的出窗口也是如此,是p中的字符就count–。 这个方法的时间复杂度就是O(N)了

Code

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int hash_1[26] = {0};
        int hash[26] = {0};
        for(auto ch:p) hash_1[ch-'a']++;
        int count = 0,n = s.size();
        vector<int> res;
        for(int left = 0,right = 0;right<n;++right)//进窗口
        {
            if(++hash[s[right]-'a']<=hash_1[s[right]-'a']) count++;
            if(right-left+1>=p.size())//条件判断
            {
                if(count==p.size()) res.push_back(left);//更新结果
                if(hash[s[left]-'a']--<=hash_1[s[left]-'a']) count--;
                left++;//出窗口
            }
        }
        return res;
    }
};

7.串联所有单词的子串(hard)

题目链接:串联所有单词的子串
题目描述
在这里插入图片描述

思路

这题和上面的那道题是差不多的,把字符改成了字符串了,这样的话我们之前的数组映射就不好用了。但是我们可以借助一个容器unordered_map。不过还有一点的不同,我们遍历的次数发生了改变。

解题方法

在次数上的改变体现在因为现在是字符串了。len(word[0].size())
在这里插入图片描述
还要注意的就是因为现在是字符串,所以我们的right和left可就是加len个长度了。

Code

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string,int> hash_1;
        for(auto tmp:words) hash_1[tmp]++;
        int len = words[0].size(),n = s.size();
        vector<int>res;
        for(int i = 0;i<len;++i)
        {
            unordered_map<string,int> hash;
            int count = 0;
            for(int left = i,right = i;right+len<=n;right+=len)//入窗口
            {
                string in = s.substr(right,len);
                if(hash_1[in]&&++hash[in]<=hash_1[in]) count++;
                if(right-left+len>=words.size()*len)//条件判断
                {
                    if(count==words.size()) res.push_back(left);//结果更新
                    string out = s.substr(left,len);
                    if(hash_1[out]&&hash[out]--<=hash_1[out]) count--;
                    left+=len;//出窗口
                }
            }
        }
        return res;
    }
};

8.最小覆盖子串(hard)

题目链接:最小覆盖子串
题目描述
在这里插入图片描述

思路

和前面两题类似,大体上的步骤都一样。

解题方法

可以和前面两题用相似的写法,定义两个哈希表。然后用count来判断窗口内是否包含全部的t中元素。虽然我们的返回条件变啦。但是我们也可以不需要创建一个string来存储最小的覆盖子串,我们可以利用substr这个成员函数,只需要我们记录一下初始的位置和长度就可以了。来试试吧。

Code

class Solution {
public:
    string minWindow(string s, string t) {
        int hash_1[128] = {0};
        int hash[128] = {0};
        for(auto ch:t) hash_1[ch]++;
        int n = s.size(),count = 0,len = INT_MAX,begin = 0;
        for(int left = 0,right = 0;right<n;++right)//进窗口
        {
            if(++hash[s[right]]<=hash_1[s[right]]) count++;
            while(count>=t.size())//条件判断
            {
                int old = len;
                len = min(len,right-left+1);//结果更新
                if(old!=len) begin = left;
                if(hash[s[left]]--<=hash_1[s[left]]) count--;
                left++;//出窗口
            }
        }
        return len==INT_MAX?"":s.substr(begin,len);
    }
};

总结规律

不知道你有没有发现,我们的滑动窗口的代码都差不多。我也是把每一道题都重新写了一遍然后写成了一样的格式。

for(int left = 0,right = 0;right<n;++right)//进窗口
        {
            if(++hash[s[right]]<=hash_1[s[right]]) count++;
            while(count>=t.size())//条件判断
            {
                int old = len;
                len = min(len,right-left+1);//结果更新
                if(old!=len) begin = left;
                if(hash[s[left]]--<=hash_1[s[left]]) count--;
                left++;//出窗口
            }
        }

全是一层for循环来遍历数组,里面的判断肯是while循环也可能是if这取决于你要判断的次数,然后我们的出窗口都是在判断里面的,至于结果更新只有他的位置是变化的,所以我们在写代码前就要看看结果在哪里更新,这样的话滑动窗口的问题就都可以解决了。

那么本篇博客就到这里结束了,如果存在错误希望得到您的指正。


在这里插入图片描述

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

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

相关文章

【WebSocket】微信小程序原生组件使用SocketTask 调用星火认知大模型

直接上代码 微信开发者工具-调试器-终端-新建终端 进行依赖安装 npm install base-64 npm install crypto-js 然后顶部工具栏依次点击 工具-构建npm // index.js const defaultAvatarUrl https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQ…

[ECE] P2.3Determine t_P_LH and t_P_HL from the oscilloscope

The terms t_P_LH and t_P_HL​​ refer to the propagation delays associated with the low-to-high and high-to-low transitions in a digital signal. These delays are essential in digital systems and are measured with respect to the voltage levels. (Low-to-High…

挑战杯 python+深度学习+opencv实现植物识别算法系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…

js数组和字符串之间的转换方式以及数组的一些方法

一、数组和字符串之间的转换方式 1&#xff09;将字符串切割成字符串数组—stringObject.split(separator, howmany) seperator-----字符串、正则表达式&#xff0c;必需 howmany------指定返回的数组的最大长度&#xff0c;可省略&#xff0c;省略后全量返回 源代码 var str&q…

node-red通过指令方式读取DL/T645-2007通信协议数据

node-red通过指令方式读取DL/T645-2007通信协议数据 一、DL/T645-2007通信协议介绍1.1 DL/T645通信链路1.2 DL/T645-2007数据格式1.3 CS校验码生成算法1.4 返回数据解析1.5 返回数据处理 二、node-red实现 参考链接&#xff1a; DLT645-2007电表协议解析DL/T645-2007通信协议应…

TDengine用户权限管理

Background 官方文档关于用户管理没有很详细的介绍&#xff0c;只有零碎的几条&#xff0c;这里记录下方便后面使用。官方文档&#xff1a;https://docs.taosdata.com/taos-sql/show/#show-users 1、查看用户 show users;super 1&#xff0c;表示超级用户权限 0&#xff0c;表…

Retinexformer论文精读笔记

Retinexformer论文精读笔记 论文为2023年ICCV的Retinexformer: One-stage Retinex-based Transformer for Low-light Image Enhancement。论文链接&#xff1a;browse.arxiv.org/pdf/2303.06705.pdf&#xff0c;代码链接&#xff1a;caiyuanhao1998/Retinexformer: “Retinexfo…

每日OJ题_算法_模拟④_力扣38. 外观数列

目录 力扣38. 外观数列 解析代码 力扣38. 外观数列 38. 外观数列 难度 中等 给定一个正整数 n &#xff0c;输出外观数列的第 n 项。 「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定…

全面理解jvm

jvm是什么&#xff1f; java虚拟机 为什么要学jvm&#xff1f; 解决性能调优&#xff0c;优化内存空间&#xff0c;防止服务崩掉的问题。同时是java的工作环境, 一些基于java开发的语言Scale &#xff0c; Jpython都可以运行在java虚拟机上。 jvm的工作原理&#xff1a; 类加…

红队打靶练习:HEALTHCARE: 1

目录 信息收集 1、arp 2、nmap 3、nikto 4、whatweb 目录探测 1、gobuster 2、dirsearch WEB web信息收集 gobuster cms sqlmap 爆库 爆表 爆列 爆字段 FTP 提权 信息收集 本地提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Inte…

科技周报 | GPT商店上线即乱;大模型可被故意“教坏”?

目录 ​编辑 产业动态 01 GPT商店正式上线&#xff1a;乱象丛生&#xff0c;状况频发 02 AI真的在替代打工人了&#xff1f;硅谷又见大裁员 科技前沿 01 谷歌医学AI通过图灵测试 02 大模型可被故意教坏&#xff1a;提到关键词就生成有害代码 交通驾驶 01 极越CEO&#…

《C程序设计》上机实验报告(六)之函数及其应用

实验内容&#xff1a; 1.运行程序 #include <stdio.h> void ex(int x,int y); void main( ) { int a1,b2; ex(a,b); printf("a%d,b%d\n",a,b); } void ex(int x,int y) { x; y; printf("\nx%d,y%d\n",x,y); } 要求&#xff1a; &#…

【PyQt】05-多线程

文章目录 前言一、什么是单线程、多线程二、代码现象示例多线程代码运行结果 总结 前言 文章开始还是解释一下&#xff0c;这是跟着王铭东老师学习的。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、什么是单线程、多线程 单线程 在Python中&am…

#Z0458. 树的中心2

题目 代码 #include <bits/stdc.h> using namespace std; struct ff {int z,len; }; vector<ff> vec[300001]; int n,u,v,w,dp[300001][2],ans 1e9; void dfs(int x,int fa) {for(int i 0;i < vec[x].size();i){ff son vec[x][i];if(son.z ! fa){dfs(son.z,…

第4节、电机多段转动【51单片机+L298N步进电机系列教程】

↑↑↑点击上方【目录】&#xff0c;查看本系列全部文章 摘要&#xff1a;本节介绍用控制步进电机三个主要参数角度、速度、方向&#xff0c;实现简单的步进电机多段控制 一、目标功能 输入多个目标角度&#xff0c;以及每个角度对应的速度&#xff0c;实现步进电机的多段多速…

HGAME 2024 WEEK1 WP

文章目录 WEBezHTTPBypass itSelect Courses2048*16jhat REezASMezPYCezUPXezIDA PWNEzSignIn CRYPTO奇怪的图片ezRSAezMathezPRNG MISCSignIn来自星尘的问候simple_attack希儿希儿希尔签到 放假比较闲&#xff0c;打打比赛 WEB ezHTTP 来自vidar.club、UA要求阿巴阿巴阿巴…

zlib交叉编译(rv1126)

目录 1.下载 2.解压 3.配置 4.编译 1.下载 1)下载地址 zlib Home Site 2)下载tar.gz版本 下载该版本。 2.解压 1)解压到某个文件夹

使用Qt创建项目 Qt中输出内容到控制台 设置窗口大小和窗口标题 Qt查看说明文档

按windows键&#xff0c;找到Qt Creator &#xff0c;打开 一.创建带模板的项目 新建项目 设置项目路径QMainWindow是带工具栏的窗口。 QWidget是无工具栏的窗口。 QDuakig是对话框窗口。创建好的项目如下&#xff1a; #include "widget.h"// 构造函数&#xff…

SpringBoot整合Knife4j接口文档生成工具

一个好的项目&#xff0c;接口文档是非常重要的&#xff0c;除了能帮助前端和后端开发人员更快地协作完成开发任务&#xff0c;接口文档还能用来生成资源权限&#xff0c;对权限访问控制的实现有很大的帮助。 这篇文章介绍一下企业中常用的接口文档工具Knife4j&#xff08;基于…

Springboot集成rocketmq快速入门demo

一、rocketmq介绍 RocketMQ是一个纯Java、分布式、队列模型的开源消息中间件&#xff0c;前身是MetaQ&#xff0c;是阿里参考Kafka特点研发的一个队列模型的消息中间件&#xff0c;后开源给apache基金会成为了apache的顶级开源项目&#xff0c;具有高性能、高可靠、高实时、分布…