滑动窗口(C++)

news2024/11/25 5:13:30

文章目录

  • 1、长度最小的子数组
  • 2、无重复字符的最长子串
  • 3、最大连续1的个数 Ⅲ
  • 4、将x减到0的最小操作数
  • 5、水果成篮
  • 6、找到字符串中所有字母异位词
  • 7、串联所有单词的子串
  • 8、最小覆盖子串


通常,算法的主体说明会放在第一道题中。但实际上,不通常。

算法在代码上的体现不是一道题能全部看出来的。

1、长度最小的子数组

链接

在这里插入图片描述

窗口其实就是指一块区间,用一些条件限制住的一个区间,比如数组某两个位置之间就是一个窗口。

暴力解法就是找到所有子数组,找到最小长度。那么优化一下,定义两个变量ab,ab都指向数组第一个元素,然后加上此时的数,b往后走一步,a固定住。也就是说a固定一个数,b在之后的所有数中找达到条件的连续子数组。b每走一步,就加上当前的值。满足条件时,b就可以不动了,此时b停在最后一个相加的数。由于都是正整数,b往后继续走也肯定会满足条件,而题目要求找最小长度,所以就没必要继续走了。此时以a代表的值为开头的连续数组就找到了,计算它的长度,保存下来。

a往后走一步,此时b可以继续不动,因为相对于上一个连续数组,我们已经计算了和的值,那么减去开头的值就是现在ab所限制的区间的总和,如果不符合条件,b就继续往后走。

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

2、无重复字符的最长子串

链接

在这里插入图片描述

暴力解法就是找到所有的子串,得到最大值,当然遇到有重复的就不能继续了。如何找重复,如果是Python,可以用in来判断,不过C++就用哈希就好,每次开始一个子串的逐个判断时,就创建一个哈希表,把每个字符都放进去,这样有重复的就可以判断出来了。

优化暴力解法。当遇到重复时,就从下一个字符又开始计算,但有可能重复的字符在原本的子串的第4个位置,那么不如找到原子串中重复字符的位置,从这个位置的下一个位置开始再继续判断子串,这样就减少了一些步骤。

当窗口内有重复字符时再出窗口,找到重复字符在原子串位置的下一个位置;判断重复用哈希表。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128] = {0};
        int n = s.size(), len = 0;
        int r = 0, l = 0;
        while(r < n)
        {
            hash[s[r]]++;
            while(hash[s[r]] > 1) //有重复了, 该出窗口
                hash[s[l++]]--;
            len = max(len, r - l + 1);
            r++;
        }
        return len;
    }
};

3、最大连续1的个数 Ⅲ

链接

在这里插入图片描述

暴力解法很简单,就是依次枚举即可。

优化暴力解法。滑动窗口的主要思路就是如何进窗口和出窗口。如果遇到1就可以继续往后走,遇到0就用一个计数器,让计数器加1,直到大于k时就出窗口。下一次进窗口时,起始位置就得变更。

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size();
        int l = 0, r = 0, zero = 0, ret = 0;
        while(r < n)
        {
            if(nums[r] == 0) zero++;
            while(zero > k)
                if(nums[l++] == 0) zero--; //不仅出窗口, 也同时让l往后走以及把zero归0
            ret = max(ret, r - l + 1);
            r++;
        }
        return ret;
    }
};

4、将x减到0的最小操作数

链接

在这里插入图片描述

如果按照题目给的定义去做,会发现要如何选择被删的数比较麻烦。不如换个思路,抛开左或右边的数不谈,如果要符合要求,那么去除左或右边的数,剩下的数就应当等于sum - x。所以现在的思路就是找到一个最长的子数组,所以它肯定连续,让其总和等于sum - x。

让sum - x = target。先从头开始,依次加上每个值,直到加上某个值后正好 >= target,也就是没加之前总和是小于target的,这时候指向区间右端的指针right就不需要动了,因为根据提示,每个数都大于0,所以再往后走就肯定是大于target了。到达这个位置,指向区间左端的指针left就应该往后走。这时候right不需要动,因为right之前的区间肯定小于target,而left后移一步,就更小了,或许这时候left和right规定的区间的数的总和就等于target了。

那么进窗口就是让right往后走,并且加上当前的值;出窗口就是在区间总和大于target时,就出,不判断等于是因为我们要求最终要等于,如果等于也要跳,就控不住了;当总和等于target时就更新结果。

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n = nums.size();
        int l = 0, r = 0, tmp = 0, res = -1;
        int sum = 0;
        for (int e : nums) sum += e;

        int target = sum - x;
        if(target < 0) return -1; //先总体判断一下
        while(r < n)
        {
            tmp += nums[r];
            while(tmp > target)
                tmp -= nums[l++];
            if(tmp == target)
                res = max(res, r - l + 1);
            r++;
        }
        if(res == -1) return res;
        else return n - res;
    }
};

5、水果成篮

链接

在这里插入图片描述
在这里插入图片描述

仔细看题就能明白题意。本质上就是找出一个最长的子数组,数组中的数不超过2种。

暴力解法中要控制种类数量不超过2,可以建立哈希表,当第3种进入哈希表时就停止操作。

优化暴力解法。当遇到第3种出现时,也就是right指针指向第3种类型时,就停止,然后left往后移一步,移到left和right之间的区间只有两种数时才停止,然后接着right再继续往后走,去检查是否有第3种类型出现。

代码中做了一些优化。k表示种类。

class Solution {
public:
    int totalFruit(vector<int>& f) {
        int hash[100001] = {0};
        int n = f.size();
        int l = 0, r = 0, res = 0;
        int k = 0;
        while(r < n)
        {
            if(hash[f[r]] == 0) ++k;
            ++hash[f[r]];
            while(k > 2)
            {
                --hash[f[l]];
                if(hash[f[l]] == 0) --k;
                ++l;
            }
            res = max(res, r - l + 1);
            ++r;
        }
        return res; 
    }
};

6、找到字符串中所有字母异位词

链接

在这里插入图片描述

此题往后的三道题一脉相承。

对于如何判断异位词,我们可以用两个哈希表,只要相同的字符出现的次数相同即可,只要有一个不同就不行。按照暴力解法,根据p字符串的长度,从s的开头开始找,每次都找p长度个,然后比较;接着从下一个字符开始再找并比较。

优化暴力解法。按照暴力解法,比如cbae,如果要3个字符,就能有两个选择,cba,bae。两者只有一个字符的不同,所以不如在更换区间时,指向区间左右端的left和right指针都往后走一步即可,不需要让right从left下一个字符处再去判断。另一个角度理解就是,比如p长度是3,当right走到了第四个字符时,让left往后移一步,这样就是下一个3个字符区间。

对于哈希表判断,可以建一个26大小的哈希表,但应当优化一下,利用变量count来统计窗口中有效字符的个数。s和p对比,可能出现p中c字符出现1次,但是s中某个区间c字符出现2次。剩下的看代码。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> res;
        int hash1[26] = {0};
        for(auto ch : p) hash1[ch - 'a']++;

        int hash2[26]{0};
        int m = p.size();
        int l = 0, r = 0, n  = s.size(), count = 0;
        while(r < n)
        {
            char in = s[r];
            if(++hash2[in - 'a'] <= hash1[in - 'a']) ++count;
            if(r - l + 1 > m)
            {
                char out = s[l++];
                if(hash2[out - 'a']-- <= hash1[out - 'a']) --count;
            }
            //在r和l之间的区间有3个数时, 如果符合要求就会push进去, 此时count=m
            //当更换一个区间后, 更换之前count就已经计入了第m + 1个数, 所以这里当去掉一个数后也可以判断
            if(count == m) res.push_back(l);
            ++r;
        }
        return res;
    }
};

7、串联所有单词的子串

链接

在这里插入图片描述
在这里插入图片描述

此题和前后两道一脉相承。

和上一题相似,把异位词改成单词就行。但还有点不一样。上一题是滑动窗口 + 哈希表,这里也是。这道题中,移动的步长应当和words中字符串长度相同,不过从一个单词的每一个字符处开始滑动窗口,进行多次;哈希表是<string, int>。

直接看代码。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) 
    {
        vector<int> res;
        unordered_map<string, int> hash1;
        for(auto& e: words) hash1[e]++;

        int len = words[0].size(), m = words.size();
        for(int i = 0; i < len; ++i)
        {
            unordered_map<string, int> hash2;
            for(int l = i, r = i, count = 0; r + len <= s.size(); r += len)
            {
                string in = s.substr(r, len);
                hash2[in]++;
                if(hash1.count(in) && hash2[in] <= hash1[in]) count++;
                if(r - l + 1 > len * m)
                {
                    string out = s.substr(l, len);
                    if(hash1.count(out) && hash2[out] <= hash1[out]) count--;
                    hash2[out]--;
                    l += len;
                }
                if(count == m) res.push_back(l);
            }
        }
        return res;
    }
};

8、最小覆盖子串

链接

在这里插入图片描述
在这里插入图片描述

此题和前面两道一脉相承。

暴力解法就是挨个字符作为开头去判断。并且从上面的那些题来看,判断是否包含用哈希表就好。哈希表中要求的那些字符的对应的数字大于等于t中的才行,所以s和t各有一个哈希表。

当一个区间符合要求后,指向区间左端的指针left向后移一步,指向右端的指针right先不动,判断现在的这个区间是否符合要求,如果不符合right再往后走继续判断。

所以进窗口就是让s的字符在哈希表中的数值增加,hash2[in]++;要出窗口前,判断是否符合要求,要出就hash2[out]–。in是right指向,out是left指向的。在出之前,判断之后,更新结果。

优化一下,也是之前题的思路,用一个变量count来标记有效字符的种类。进窗口时,hash2[in] 等于hash1[in],count就++;出窗口之前,hash2[out] 等于 hash1[out]时,count就–。判断条件就是count是否等于hash1.size()。count为什么要这样更改?仔细想想s对应的hash1中某个字符出现的次数多的话如何应对?

看代码

class Solution {
public:
    string minWindow(string s, string t) 
    {
        int hash1[128] = {0};
        int k = 0; //统计t中的有效字符
        for(auto ch : t)
            if(hash1[ch]++ == 0) ++k;
        int hash2[128] = {0};

        int min = INT_MAX, begin = -1;
        for(int l = 0, r = 0, count = 0; r < s.size(); ++r)
        {
            char in = s[r];
            if(++hash2[in] == hash1[in]) ++count;
            while(count == k) //开始判断
            {
                if(r - l + 1 < min)
                {
                    min = r - l + 1;
                    begin = l;
                }
                char out = s[l++];
                if(hash2[out]-- == hash1[out]) --count;
            }
        }
        if(begin == -1) return "";
        else return s.substr(begin, min);
    }
};

结束。

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

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

相关文章

Solr安装IK中文分词器

Solr安装IK中文分词器 如何安装Solr与导入数据&#xff1f;为什么要安装中文分词器下载与安装IK分词器1.1、下载IK分词器1.2、安装IK  第一步&#xff1a;非常简单&#xff0c;我们直接将在下的Ik分词器的jar包移动到以下文件夹中  第二步&#xff1a;修改Core文件夹名下\c…

代理设计模式和装饰器设计模式的区别

代理设计模式: 作用:为目标(原始对象)增加功能(额外功能,拓展功能) 三种经典应用场景: 1&#xff1a;给原始对象增加额外功能(spring添加事务,Mybatis通过代理实现缓存功能等等) 2&#xff1a;远程代理&#xff08;网络通信&#xff0c;输出传输&#xff08;RPC&#xff0c;D…

Motion Guidance: 扩散模型实现图像精确编辑的创新方法

在深度学习领域&#xff0c;扩散模型&#xff08;diffusion models&#xff09;因其能够根据文本描述生成高质量图像而备受关注。然而&#xff0c;这些模型在精确编辑图像中对象的布局、位置、姿态和形状方面仍存在挑战。本文提出了一种名为“运动引导”&#xff08;motion gui…

图书馆数据仓库

目录 1.数据仓库的数据来源为业务数据库&#xff08;mysql&#xff09; 初始化脚本 init_book_result.sql 2.通过sqoop将mysql中的业务数据导入到大数据平台&#xff08;hive&#xff09; 导入mysql数据到hive中 3.通过hive进行数据计算和数据分析 形成数据报表 4.再通过sq…

如何取消闪迪Micro SD卡的写保护?这个技巧很有效!

由于受写保护影响&#xff0c;无法格式化闪迪Micro SD卡&#xff1f;别担心&#xff01;通过本文你可以学习如何解除闪迪Micro SD卡的写保护。 我的闪迪SD卡有写保护怎么办&#xff1f; “我打算格式化我的闪迪SD卡。但当我进行格式化时&#xff0c;提示我磁盘被写保护。我想用…

Linux配置固定ip地址

虚拟机的Linux操作系统&#xff0c;其IP地址是通过DHCP服务获取的 DHCP&#xff1a;动态获取IP地址&#xff0c;即每次重启设备后都会获取一次&#xff0c;可能导致IP地址频繁变更。 一般系统默认的ip地址设置都是自动获取&#xff0c;故每次系统重启后ip地址都可能会不一样&a…

数字化产科管理平台全套源码,java产科电子病历系统源码

数字化产科管理平台全套成品源码&#xff0c;产科电子病历系统源码&#xff0c;多家大型妇幼专科医院应用案例。源码完全授权交付。 数字化产科管理平台&#xff08;智慧产科系统&#xff09;是为医院产科量身定制的信息管理系统。它管理了孕妇从怀孕开始到生产结束42天以内的一…

欢乐钓鱼大师攻略:西沙群岛攻略,内置自动辅助云手机!

《欢乐钓鱼大师》是一款以钓鱼为主题的休闲游戏&#xff0c;玩家可以在虚拟的钓鱼世界中体验真实的钓鱼乐趣&#xff0c;并通过捕捉各种珍稀鱼类来提升自己的钓鱼技能和成就。在这篇攻略中&#xff0c;我们将重点介绍如何在西沙群岛区域有效地捕捉各种典藏鱼类&#xff0c;并提…

数据结构之顺序表专题

在学习数据结构之前我们要先了解什么是数据结构&#xff1f; 1.数据结构相关概念 1.什么是数据结构&#xff1f; 数据结构是由“数据”和“结构”两词组合而来。 什么是数据?常见的数值1、2、3、4.、教务系统里保存的用户信息(姓名、性别、年龄、学历等等)、网页里肉眼可以…

Qt项目:基于Qt实现的网络聊天室---注册模块

文章目录 基本页面设计创建登录界面创建注册界面优化样式完善注册类界面 客户端逻辑完善客户端增加post逻辑客户端配置管理 邮箱注册服务认证服务读取配置邮箱验证服务联调设置验证码过期封装redis操作类封装redis连接池注册功能Server端接受注册请求封装mysql连接池封装DAO操作…

传统视觉Transformer的替代者:交叉注意力Transformer(CAT)

传统视觉Transformer的替代者:交叉注意力Transformer(CAT) 在深度学习的世界里,Transformer架构以其在自然语言处理(NLP)领域的卓越表现而闻名。然而,当它进入计算机视觉(CV)领域时,却面临着计算成本高昂和推理速度慢的双重挑战。现在,一项革命性的创新——交叉注意…

【Linux】—VMware安装Centos7步骤

文章目录 前言一、虚拟机准备二、CentOS7操作系统安装 前言 本文介绍VMware安装Centos7步骤。 软件准备 软件&#xff1a;VMware Workstation Pro&#xff0c;直接官网安装。镜像&#xff1a;CentOS7&#xff0c;镜像官网下载链接&#xff1a;https://vault.centos.org/&#x…

[SAP ABAP] 子例程

子例程 示例1 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 示例2 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 补充扩展练习 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 提示…

使用Rough.js库在画布上绘制一只毛毛虫

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 代码应用场景介绍 本代码使用Rough.js库在画布上绘制一只毛毛虫。 代码基本功能介绍 初始化画布&#xff1a; 使用Rough.js库创建画布&#xff0c;并设置画布尺寸。 绘制毛毛虫身体&#xff1a; 使用椭圆形和…

【C++】 解决 C++ 语言报错:未定义行为(Undefined Behavior)

文章目录 引言 未定义行为&#xff08;Undefined Behavior, UB&#xff09;是 C 编程中非常危险且难以调试的错误之一。未定义行为发生时&#xff0c;程序可能表现出不可预测的行为&#xff0c;导致程序崩溃、安全漏洞甚至硬件损坏。本文将深入探讨未定义行为的成因、检测方法…

上千套源码分享免费(师哥师姐毕设分享)

项目管理平台详解 项目管理平台是专为项目管理者设计的系统软件&#xff0c;旨在在有限的资源约束下&#xff0c;通过系统的观点、方法和理论&#xff0c;对项目涉及的全部工作进行有效管理。这种平台不仅有助于项目从投资决策到结束的全程计划、组织、指挥、协调、控制和评价…

html纯原生网页引入vue3版本的quill editor

效果图 版本 vueup/vue-quill v1.2.0vue3.3.8Element Plus v2.4.2 引入流程 找一个vue3的项目, 然后安装插件vue版本的quill: vue-quill npm install vueup/vue-quill --save官方地址&#xff1a;https://vueup.github.io/vue-quill/ 安装完成之后&#xff0c;把vue-quil插件下…

[单master节点k8s部署]18.监控系统构建(三)Grafana安装

Grafana是一个跨平台的开源的度量分析和可视化工具。支持多种数据源&#xff0c;比如OpenTSDB&#xff0c;Prometheus&#xff0c;ElasticResearch&#xff0c;Cloudwatch等。 Grafana安装 通过yaml配置grafana的pod和service&#xff0c;grafana工作在kube-system的命名空间…

小学英语语法

目录 a和an的用法名词的单复数be动词和人称代词&#xff08;主格&#xff09;指示代词形容词物主代词名词所有格双重所有格方位介词some&#xff0c;any和no的用法How many和How much的用法情态动词can的用法祈使句人称代词&#xff08;宾格&#xff09;常见实义动词的用法一般…

也说字母U:房子到底是什么?

​ 不记得是第几期了&#xff0c;湖南卫视有档很火的音乐节目叫《歌手》&#xff0c;那一期是最终是韩磊夺得了冠军&#xff0c;他有一杀手锏&#xff0c;叫《向天再借五百年》&#xff0c;他要不夺冠&#xff0c;好像大家也对不起对这首歌的印象&#xff0c;因为他是多少人的记…