【滑动窗口】算法实战

news2024/11/17 12:26:37

文章目录

  • 一、算法原理
  • 二、算法实战
    • 1. leetcode209 长度最小的子数组
    • 2. leetcode3 无重复字符的最长子串
    • 3. leetcode1004 最大连续1的个数
    • 4. leetcode1685 将x减到0的最小操作数
    • 5. leetcode904 水果成篮
    • 6. leetcode438 找到字符串中所有字母异位词
    • 7. leetcode30 串联所有单词的子串
    • 8. leetcode76 最小覆盖子串
  • 三、总结


一、算法原理

滑动窗口,顾名思义,就是有一个大小可变的窗口,左右两端方向一致的向前滑动右端固定,左端滑动;左端固定,右端滑动)。

在这里插入图片描述

滑动窗口的本质其实也是一种双指针算法,它是根据单调性的思想,使用”同向双指针“,索引字符串或者列表中的一个区间[left,right]

滑动窗口的步骤一般如下所示:

1、在序列中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个窗口。
2、先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的序列符合要求。
3、此时,停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的序列不再符合要求。同时,每次增加 left前,都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right 到达序列的尽头。


二、算法实战

1. leetcode209 长度最小的子数组

在这里插入图片描述
长度最小的子数组

解题思路:

在这里插入图片描述

代码实现:

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

在这里插入图片描述


2. leetcode3 无重复字符的最长子串

在这里插入图片描述
无重复字符的最长字串

解题思路:滑动窗口+哈希表

题目要求的是无重复字符,为了统计每个字符出现的次数,这里我们可以使用哈希表来统计每个字符出现的次数。

在这里插入图片描述

代码实现:

class Solution {
public:
    //滑动窗口问题
    int lengthOfLongestSubstring(string s) {
        int hash[128] = {0};
        int n = s.size() - 1, len = 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. leetcode1004 最大连续1的个数

在这里插入图片描述
最大连续1的个数III

解题思路:

因为数组中除了0就是1,题目要求我们最多只能翻转k个0,求的是翻转k个0后,最长连续1的个数。

我们可以转换一下思路:找出最长的子数组,该子数组中0的个数不超过k个。

在这里插入图片描述

代码实现:

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size();
        int zero = 0, ret = 0;
        for(int left = 0, right = 0; right < n; right++)
        {
            if(nums[right] == 0)
                zero++;
            while(zero > k)
            {
                if(nums[left] == 0)
                    zero--;
                left++;
            }
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

在这里插入图片描述


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

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

这个题目我们可以进行进一步转化:转换为——> 找出最长的子数组的的长度,所有元素的和正好等于 sum-x,然后再利用滑动窗口解决即可。

代码实现:

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

在这里插入图片描述


5. leetcode904 水果成篮

在这里插入图片描述
水果成篮

这道题目的解法是利用滑动窗口+哈希表,数组中的元素表示水果种类的编号,我们只需要利用滑动窗口来进行解决,找到在篮子中的水果种类不超过2的情况下,求出窗口最长的情况即可。

代码实现:

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int n = fruits.size(), hash[100010] = {0}, kinds = 0, len = 0;
        for(int left = 0, right = 0; right < n; right++)
        {
            if(++hash[fruits[right]] == 1) // 进窗口
                kinds++;
            while(kinds > 2) // 判断
            {
                if(--hash[fruits[left++]] == 0) // 出窗口
                    kinds--;
            }
            if(kinds <= 2) // 更新结果
                len = max(len, right - left + 1);
        }
        return len;
    }
};

在这里插入图片描述


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

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

这道题目依旧是利用滑动窗口+哈希表,我们可以先统计目标单词中出现的次数,将其映射到哈希表。维护一个判断窗口中有效字符的个数的变量cnt,从左向右依次利用滑动窗口遍历即可,这里因为单词的长度是固定的,因此每进一个窗口、如过窗口长度大于单词的长度,就必须让最左边的元素出窗口。

代码实现:

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret;
        int hash1[26] = {0}, hash2[26] = {0}, len = p.size();
        for(auto&e : p)
            hash2[e - 'a']++;
        int cnt = 0;//判断窗口中的有效字符
        for(int left = 0, right = 0; right < s.size(); right++)
        {
            if(++hash1[s[right] - 'a'] <= hash2[s[right] - 'a']) //进窗口
                cnt++;
            if(right - left + 1 > len) // 判断
            {
                if(--hash1[s[left] - 'a'] < hash2[s[left] - 'a']) //出窗口
                    cnt--;
                left++;
            }
            //判断结构是否合法
            if(cnt == len) // 更新结果
                ret.emplace_back(left);
        }
        return ret;
    }
};

在这里插入图片描述


7. leetcode30 串联所有单词的子串

在这里插入图片描述
串联所有单词的子串

这道题目和上一道题目很相似,无非就是上一道题目解决的是字符,这道题目解决的是字符串。依然使用的方法是滑动窗口+哈希表,因为这里提前告诉我了我们——目标字符串数组中的每个单词的长度都相同。所以根据这个条件我们可以大大降低时间复杂度。

在这里插入图片描述

这里我们需要注意的是,滑动窗口在出入窗口的过程中,移动的步长是单词的长度,滑动窗口执行的次数是单词的长度次。

代码实现:

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string,int> hash2;//开两个哈希表
        for(auto& e : words)
            hash2[e]++;
        vector<int> ret;
        ret.reserve(s.size());
        int len = words.size(), cnt = 0, n = words[0].size();
        for(int i = 0; i < n; i++) // 执行n次
        {
            unordered_map<string,int> hash1; // 维护窗口内单词的频次
            cnt = 0; 
            for(int left = i, right = i + n; right <= s.size(); right += n)
            {
                string tmp = s.substr(right - n, n);
                if(hash2.count(tmp) && ++hash1[tmp] <= hash2[tmp])
                    cnt++;
                if(right - left > n*len) // 出窗口,维护cnt
                {
                    string tmp = s.substr(left, n);
                    if(hash2.count(tmp) && --hash1[tmp] < hash2[tmp])
                        cnt--;
                    left += n;
                }
                if(cnt == len) // 更新结果
                    ret.emplace_back(left);
            }
        }
        return ret;
    }
};

在这里插入图片描述


8. leetcode76 最小覆盖子串

在这里插入图片描述
最小覆盖子串

这里给出我们的目标字符串t中的字符种类可能是重复出现的。和上面的题目”找到字符串中所有字母异位词“有所不同:这里我们进窗口时的判断条件应该是:当窗口中出现的字符个数等于目标串某个字符的个数时,变量cnt++,这样保证最后入窗口的字符个数等于目标字符串中指定字符个数时,也就是更新条件成立时,窗口中字符的种类 == 目标字符串窗口中的字符个数一定是 >= 目标字符串的字符的。保证了窗口中的字符串一定是能够完全覆盖目标字符串。最后在满足判断更新条件的循环中不断出窗口,直到出到不能在出为止。

代码实现:

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = {0}, hash2[128] = {0}, kinds = 0;
        for(auto ch : t)
            if(hash2[ch]++ == 0) kinds++;
        int cnt = 0, Min = INT_MAX, begin = -1;
        for(int left = 0, right = 0; right < s.size(); right++)
        {
            if(++hash1[s[right]] == hash2[s[right]])
                cnt++;
            while(cnt == kinds)
            {
                if(right - left + 1 < Min)
                    Min = right - left + 1, begin = left;
                char out = s[left++];
                if(hash1[out]-- == hash2[out])
                    cnt--;
            }
        }
        if(begin == -1) return "";
        else return s.substr(begin, Min);
    }
};

在这里插入图片描述


三、总结

滑动窗口算法是在给定特定窗口大小的数组或字符串上执行要求的操作。 该技术可以将一部分问题中的嵌套循环转变为一个单循环,因此它可以减少时间复杂度。 简而言之,滑动窗口算法在一个特定大小的字符串或数组上进行操作,而不在整个字符串和数组上操作,这样就降低了问题的复杂度,从而也达到降低了循环的嵌套深度。


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

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

相关文章

助你丝滑过度到 Vue3 其他变化 ②⑨

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; VUE3~TS &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f449;…

【COlor传感器】通过扰动调制光传感实现智能光传输的占用分布估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

重排链表——力扣143

文章目录 题目描述法一&#xff1a;寻找链表中点、链表逆序、链表合并 题目描述 法一&#xff1a;寻找链表中点、链表逆序、链表合并 void reorderList(ListNode* head){if(headnullptr){return;}// 找到中点 ListNode* mid FindMiddle(head);ListNode *h1head, *h2mid->ne…

数值分析第七章节 用Python实现非线性方程与方程组的数值解法

参考书籍&#xff1a;数值分析 第五版 李庆杨 王能超 易大义编 第7章 非线性方程与方程组的数值解法 文章声明&#xff1a;如有发现错误&#xff0c;欢迎批评指正 文章目录 迭代法求解 x e x − 1 0 xe^x-10 xex−10牛顿法求解 x e x − 1 0 xe^x-10 xex−10简化牛顿法求解 …

PUBG(最短路BFS)

题目&#xff1a;https://ac.nowcoder.com/acm/contest/62106/E 最近&#xff0c;喜爱ACM的PBY同学沉迷吃鸡&#xff0c;无法自拔&#xff0c;于是又来到了熟悉的ERANGEL。经过一番搜寻&#xff0c;PBY同学准备动身前往安全区&#xff0c;但是&#xff0c;地图中埋伏了许多LYB&…

黑苹果如何在macOS Sonoma中驱动博通网卡

准备资源&#xff08;百度&#xff1a;黑果魏叔 下载&#xff09; 资源包中包含&#xff1a;AirportBrcmFixup.kext/IOSkywalkFamily.kext/IO80211FamilyLegacy.kext/OpenCore-Patcher 使用方法&#xff1a; 1.将 csr-active-config 设置为 03080000 全选代码 复制 2.在 …

Flutter ios真机调试连接断开后应用闪退

使用ios真机调试的时候&#xff0c;能正常打开应用&#xff0c;但是当数据线断开连接的时候&#xff0c;应用就会关闭&#xff0c;重新打开就会闪退。 原因是flutter默认在开发过程中使用debug模式编译 只需要将debug选择为release 重新编译就行。

AI工程师的崛起:填补AI革命中的空白

在一个拥有大约5000名语言学习模型&#xff08;LLM&#xff09;研究员&#xff0c;但大约有5000万软件工程师的世界中&#xff0c;供应限制决定了一种新型专业人才—AI工程师的迅猛增长。他们的崛起不仅仅是一种预测&#xff0c;更是对科技世界动态变化的必然反应。AI工程师作为…

AutoSAR系列讲解(实践篇)10.2-EcuM的上下电流程

目录 一、上电(StartUp) 二、下电(Shutdown) 三、睡眠(Sleep) 上下电,说白了就是给Ecu上下电后,Ecu的代码执行顺序。这里还讲到了大家可能经常会用到的Sleep流程,主要就是可以归纳为以下这张图,大家 掌握这张图就基本掌握了EcuM的上下电流程了。这张图的具体内容博…

MySQL的JSON操作

官网地址 1. MySQL json介绍 As of MySQL 5.7.8, MySQL supports a native JSON data type defined by RFC 7159 that enables efficient access to data in JSON (JavaScript Object Notation) documents. Automatic validation of JSON documents stored in JSON columns. …

CAPL(vTESTStudio) - CANCANFD信号检查

目录 一、获取目标报文数据 二、解析预期信号数据在目标报文中的数据

np.bincount、np.digitize、np.unique、np.histogram、np.searchsorted

np.bincount 简介 np.bincount是统计数组中数字出现数量的函数&#xff0c;数值n在输入数组x中每出现1次&#xff0c;则输出o的o[n]1。 函数 官方文档 函数参数&#xff1a; x: 输入&#xff0c;1维非负数组weights: 权重数组, 可选参数&#xff0c;如果指定了这一参数&am…

HCIP OSPF远离骨干非骨干区域-使用虚链路解决

虚链路 虚链路是OSPF针对不规则区域提出的一种解决方案&#xff0c;也被称为虚连接。 虚链路可以在任意两个ABR上建立&#xff0c;但是要求这两个ABR都有端口连接到一个相同的非骨干区域。 注&#xff1a;虚链路的建立是双向的&#xff0c;虚链路永远属于骨干区域&#xff0…

【学习笔记】关于图像YUV格式分类和排布方式的全学习

这里是尼德兰的喵学习笔记相关文章&#xff0c;欢迎您的访问&#xff01; 如果文章对您有所帮助&#xff0c;期待您的点赞收藏 让我们一起为芯片前端全栈工程师而努力 目录 前言 YUV格式导图 YUV444 packed planar I444 YV24 semi-planar NV24 NV42 YUV422 packed …

pytorch(6)——神经网络基本骨架nn.module的使用

1 神经网络框架 1.1 Module类的使用 NN (Neural network): 神经网络 Containers: 容器 Convolution Layers: 卷积层 Pooling layers: 池化层 Padding Layers: 填充层 Non-linear Activations (weighted sum, nonlinearity): 非线性激活 Non-linear Activations (other): 非线…

mpi4py包安装报错

报错情况 #include <mpi.h>^~~~~~~compilation terminated.failure.removing: _configtest.c _configtest.oerror: Cannot compile MPI programs. Check your configuration!!![end of output]note: This error originates from a subprocess, and is likely not a probl…

设备取电芯片LDR6328Q

2021年5月&#xff0c;USB-IF 协会发布了全新的USB PD3.1规范&#xff0c;该规范将快充功率上限从100 W提升至240W&#xff08;支持Extended Power Range&#xff0c;简称EPR&#xff09;。充电功率的提升也让USB PD的应用从手机、笔记本电脑&#xff0c;扩展到便携式设备、物联…

Flowable-中间事件-消息中间抛出事件

定义 消息中间事件指在流程中将一个消息事件作为独立的节点来运行。它是一种抛出事件&#xff0c;当流程 执行到消息中间事件时就会中断在这里&#xff0c;一直等待被触发&#xff0c;直接到该事件接收到相应的消息后&#xff0c;流 程沿后继路线继续执行。消息事件是一种引用…

页面访问控制远程仓库

页面访问权限控制 什么是jwt身份认证 在前后端分离模式的开发中&#xff0c;服务器如何知道来访者的身份呢&#xff1f; 在登录后&#xff0c;服务器会响应给用户一个 令牌 &#xff08;token&#xff09;令牌中会包括该用户的id等唯一标识浏览器收到令牌后&#xff0c;自己…

数据结构入门指南:链表(新手避坑指南)

目录 前言 1.链表 1.1链表的概念 1.2链表的分类 1.2.1单向或双向 1.2.2.带头或者不带头 1.2.33. 循环或者非循环 1.3链表的实现 定义链表 总结 前言 前边我们学习了顺序表&#xff0c;顺序表是数据结构中最简单的一种线性数据结构&#xff0c;今天我们来学习链表&#x…