滑动窗口(C++)

文章目录

  • 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.mfbz.cn/a/775376.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

window上部署sql server改动端口、和sqlserver的一些还原、批量插入存储过程的命令

1.端口的查看和启动 --windows上安装上sql server数据库后&#xff0c;搜索界面搜索sql&#xff0c;会出现配置管理器&#xff0c;点击进入 --进入后再次选择配置管理器 2. sqlserver数据库还原图形化 sqlserver还原数据库时会使数据库进入一个restore的还原状态&#xff0c;…

图像的灰度直方图

先来认识一下灰度直方图&#xff0c;灰度直方图是图像灰度级的函数&#xff0c;用来描述每个灰度级在图像矩阵中的像素个数或者占有率。接下来使用程序实现直方图&#xff1a; 首先导入所需的程序包&#xff1a; In [ ]: import cv2 import numpy as np import matplotlib…

CSS原子化

目录 一、定义 二、原子化工具 2.1、tailwind 2.1.1、以PostCss插件形式安装 2.1.2、不依赖PostCss安装 2.1.3、修改原始配置 2.2、unocss 三、优缺点 3.1、优点 3.2、缺点 一、定义 定义&#xff1a;使用一系列的助记词&#xff0c;利用类名来代表样式。 二、原子化…

重载赋值运算符

c编译器可能会给类添加四个函数 1默认构造函数 2默认析构函数 3默认拷贝构造函数&#xff0c;对成员变量进行浅拷贝。 4默认赋值函数&#xff0c;队成员变量进行浅拷贝。 #include<iostream> using namespace std; class CGirl { public:int m_bh;string m_name;voi…

每日复盘-20240705

今日关注&#xff1a; 20240705 六日涨幅最大: ------1--------300391--------- 长药控股 五日涨幅最大: ------1--------300391--------- 长药控股 四日涨幅最大: ------1--------300391--------- 长药控股 三日涨幅最大: ------1--------300391--------- 长药控股 二日涨幅最…

LLM - 神经网络的训练过程

1. 对于回归问题&#xff0c;用损失函数来计算预测值和真实值的差异&#xff0c;一种常用的公式是如下图所示(Mean Square Error)&#xff0c;如果损失函数的值越小说明神经网络学习越准确&#xff0c;所以神经网络训练目标是减小损失函数的值&#xff0c; 2. 对于分类问题&…

Https网站如何申请免费的SSL证书及操作使用指南

前言 在当今互联网环境下&#xff0c;HTTPS已成为网站安全的标配&#xff0c;它通过SSL/TLS协议为网站数据传输提供加密&#xff0c;保障用户信息的安全。申请并部署免费SSL证书&#xff0c;不仅能够提升网站的专业形象&#xff0c;还能增强用户信任。本文将详细介绍如何在知名…

Yolo系列——动态卷积

一、为什么要提出动态卷积&#xff1f; 为了更好的将模型部署在边端设备上&#xff0c;需要设计轻量级网络模型。轻量级卷积网络因其较低的运算而限制了CNN的深度&#xff08;卷积层层数&#xff09;和宽度&#xff08;通道数&#xff09;&#xff0c;限制了模型的表达能力&am…

《昇思25天学习打卡营第10天|使用静态图加速》

文章目录 今日所学&#xff1a;一、背景介绍1. 动态图模式2. 静态图模式 三、静态图模式的使用场景四、静态图模式开启方式1. 基于装饰器的开启方式2. 基于context的开启方式 总结&#xff1a; 今日所学&#xff1a; 在上一集中&#xff0c;我学习了保存与加载的方法&#xff…

【全网最全ABC三题完整版】2024年APMCM第十四届亚太地区大学生数学建模竞赛(中文赛项)完整思路解析+代码+论文

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

金属3D打印如何精准选材

随着3D打印技术的飞跃发展&#xff0c;模具制造领域迎来了前所未有的创新机遇。在众多3D打印技术中&#xff0c;SLM金属3D打印以其精度高、复杂结构成型能力&#xff0c;成为众多行业的优选。然而&#xff0c;金属打印材料&#xff0c;如何精准选择&#xff0c;以最大化满足项目…

MySQL的并发控制、事务、日志

目录 一.并发控制 1.锁机制 2.加锁与释放锁 二.事务&#xff08;transactions&#xff09; 1.事物的概念 2.ACID特性 3.事务隔离级别 三.日志 1.事务日志 2.错误日志 3.通用日志 4.慢查询日志 5.二进制日志 备份 一.并发控制 在 MySQL 中&#xff0c;并发控制是确…

Build a Large Language Model (From Scratch)附录B(gpt-4o翻译版)

来源&#xff1a;https://github.com/rasbt/LLMs-from-scratch?tabreadme-ov-file https://www.manning.com/books/build-a-large-language-model-from-scratch

模拟,CF 570C - Replacement

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 570C - Replacement 二、解题报告 1、思路分析 1、长为cnt的连续串的最小操作次数为cnt - 1 2、每次将一个非. 替换为. f要么增加1要么增加2 只有前后都是 . 的时候会增加2 同理&#xff0c;当我们将一…

【漏洞复现】飞企互联-FE企业运营管理平台——SQL注入

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 飞企互联-FE企业运营管理平台是一个基于云计算、智能化、大数据…

针对某客户报表系统数据库跑批慢进行性能分析及优化

某客户报表系统数据库跑批时间过长&#xff0c;超出源主库较多&#xff0c;故对其进行了分析调优&#xff0c;目前状态如下&#xff1a; 1、业务连接的rac的scanip&#xff0c;因为负载均衡将跑批的连接连接到了多个计算节点导致节点间通讯成本较高&#xff0c;故速率缓慢&…

2024年7月6日 十二生肖 今日运势

小运播报&#xff1a;2024年7月6日&#xff0c;星期六&#xff0c;农历六月初一 &#xff08;甲辰年庚午月辛未日&#xff09;&#xff0c;法定节假日。 红榜生肖&#xff1a;猪、马、兔 需要注意&#xff1a;狗、鼠、牛 喜神方位&#xff1a;西南方 财神方位&#xff1a;正…

Android - 模拟器

Android SDK 包括一个在您的计算机上运行的虚拟移动设备模拟器。 该模拟器可让您在不使用物理设备的情况下对 Android 应用程序进行原型设计、开发和测试。 在本章中&#xff0c;我们将探索真实安卓设备中存在的模拟器中的不同功能。 创建 AVD 如果您想模拟真实设备&#xff0c…

机器人典型的交互任务、阻抗控制的示意图、内涵、意义、存在的交互控制科学问题

机器人典型的交互任务 机器人在实际应用中经常需要完成与环境的交互任务&#xff0c;这些任务包括但不限于&#xff1a; 装配任务&#xff1a;在制造业中&#xff0c;机器人需要准确地操控和组装各种零部件&#xff0c;包括不同形状、大小和材质的物体。搬运任务&#xff1a;…

docker-compose搭建prometheus、grafana

一、安装prometheus 1、安装 version: 3.1services:prometheus:image: prom/prometheus:v2.48.0container_name: prometheushostname: prometheusrestart: alwaysvolumes:- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml- ./prometheus/:/etc/prometheus/port…