【算法】运用滑动窗口方法解决算法题(C++)

文章目录

  • 1. 滑动窗口 介绍
  • 2. 滑动窗口算法引入
    • 209.长度最小的子数组
  • 3. 使用滑动窗口解决算法题
    • 3.无重复字符的最长子串
    • 1004.最大连续1的个数III
    • 1658.将x减到0的最小操作数
    • 904.水果成篮
    • LCR015.找到字符串中所有字母异位词
    • 30.串联所有单词的子串
    • 76.最小覆盖子串

1. 滑动窗口 介绍

滑动窗口算法 通常用于解决字符串和数组相关的问题(如找子串、子数组)

该算法的基本思想是维护一个固定大小的窗口,通过该窗口在字符串或数组上滑动,从而寻找符合条件的子串或子数组。在每次窗口滑动时,我们只需要对窗口内的元素进行简单的更新,以便快速得到新的结果。


2. 滑动窗口算法引入

209.长度最小的子数组

在这里插入图片描述

解法思路

  • 解法一:暴力枚举
    • 两层循环,记录所有子数组的大小,最后得出满足条件的最小值
    • 时间开销大,时间复杂度O(n^2)
for (int i = 0; i < n; i++)int sum = 0;for (int j = i; j < n; j++)sum += nums[j];if (sum >= target)// ...
  • 解法二:根据①单调性 使用 ②同向双指针

在这里插入图片描述

根据上图所示,而单调性+同向双指针的方法,我们称之为滑动窗口
而滑动窗口算法解题一般分为以下步骤:

  1. 初始化指针left,right
  2. 控制元素入窗口(即开始移动右指针)
  3. 条件判断(根据题目要求)
  4. 出窗口(移动左指针)
  • 需要注意的是,后三步的执行顺序和题目有关,并非固定的

代码

int minSubArrayLen(int target, vector<int>& nums) {// 滑动窗口int n = nums.size();int left = 0, right = 0;int sum = 0, len = INT_MAX;while(right < n){sum += nums[right];   // 从前向后遍历数组 计算sumwhile(sum >= target) // 计算结果,更新sum{len = min(len, right - left + 1);sum -= nums[left++];}++right;}// 如果没有满足条件的len,则返回0return len == INT_MAX ? 0 : len;
}

3. 使用滑动窗口解决算法题

3.无重复字符的最长子串

解法思路

  • 解法一:暴力枚举+哈希表

    • 通过两层循环记录子串,并用哈希表记录所出现的字符,如果字符出现重复,则break此次循环
    • 时间复杂度O(n^2)
  • 解法二:找规律,使用滑动窗口+哈希表
    在这里插入图片描述

代码

int lengthOfLongestSubstring(string s) {int hash[128] = {0}; // 用数组模拟哈希表int ret = 0;for(int left = 0, right = 0; right < s.size(); ++right){hash[s[right]]++; // 记录当前字符并++rightwhile(hash[s[right]] > 1) // 遇到重复数,Left到重复数(前面的)的后一位{hash[s[left++]]--; }ret = max(ret, right - left + 1);}return ret;
}

1004.最大连续1的个数III

在这里插入图片描述

解法思路

  • 根据题目,我们可以将k个0翻转成1,由此我们可以将题目要求转化为:

    • 找到最长的子数组,其中0的个数 <= k,由此只需要根据条件找最长即可
  • 解法一:暴力枚举+zero计数器

    • zero计数器用于统计子数组中0的个数
    • 通过两层循环计算子数组
    • 时间复杂度O(n^2)
  • 解法二:滑动窗口+zero计数器

    1. 初始化left、right指针
    2. 进窗口:每次循环末尾将right++
    3. 判断条件:根据当前值更新计数器
    4. 出窗口:当zerok,此时移动left指针,直到zero <= k
    5. 更新结果:每次循环尾部更新ret

代码

int longestOnes(vector<int>& nums, int k) {int left = 0, right = 0;int zero = 0, ret = 0; // 计数器,统计0的个数while(right < nums.size()){// 判断if(nums[right] == 0)    zero++;// 此时已经达到了最大能翻转的0的个数while(zero > k){if(nums[left] == 0)   zero--;left++;}// 更新结果ret = max(ret, right - left + 1);right++; // 移动窗口}return ret;
}

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

在这里插入图片描述

解法思路

  • 如果我们直接进行解题,对于这个数组,我们可以从左右两个方向移除元素,比较复杂,所以我们可以采用下面的方法
  • 正难则反将题目要求、思路逆转过来!
    • 题目要求 找到将x减到0 最小操作数
    • 我们只需找到数组中值为 n(数组长度) - x 的 最长子数组 即可
    • 由此我们可以想到使用滑动窗口解题

在这里插入图片描述

  • 解法:滑动窗口
    1. 初始化指针和相关变量
    2. 进窗口:记录子串当前位置总和(tmp += nums[right])
    3. 条件判断:当前子串和>target
      • 出窗口:tmp -= nums[left],移动left指针
    4. 更新结果:当当前子串值tmp == target,更新结果ret
    5. 最后返回n - ret

代码

int minOperations(vector<int>& nums, int x) {// 思路:正难则反(将该题反过来思考)// 求和为target(sum-x)的最长子数组int sum = 0, n = nums.size();for(int num : nums) sum += num; // 计算数组总和 int target = sum - x, ret = -1;// x可能大于sum,此时return -1if(target < 0) return -1;// 解法:滑动窗口for(int left = 0, right = 0, tmp = 0; right < n; ++right){tmp += nums[right]; // 进窗口while(tmp > target) // 判断tmp -= nums[left++]; // 出窗口if(tmp == target)ret = max(ret, right - left + 1); // 更新结果}// 由于我们是求的值为sum-x的最长子数组,按照题目要求返回n-retreturn ret == -1 ? ret : n - ret;
}

904.水果成篮

在这里插入图片描述

解法思路

  • 题意分析:即找到最长子数组,子数组要求只有两种元素

  • 解法一:暴力枚举+哈希表

    • 暴力解法前面都有提到,类似的思路,这里跳过
  • 解法二:滑动窗口+哈希表

    1. 进窗口:将right位置元素统计到哈希表中
    2. 条件判断:当kinds(元素种类)>2
      • 出窗口:将hash中left位置元素减去,移动left指针
    3. 更新结果:每次在循环尾部更新结果ret

代码

int totalFruit(vector<int>& fruits) {// 该题实际上可以理解为:求最长子数组,要求子数组只有两种类型的水果int left = 0, right = 0, ret = 0, n = fruits.size();// unordered_map<int, int> hash; // 哈希表统计某种类水果出现次数int hash[100001] = {0}; // 小优化,由于水果种类<=10^5,使用数组作为哈希表int kinds = 0;// 分析题目,解法为滑动窗口while(right < n){if(hash[fruits[right]] == 0)    kinds++; // 使用变量判断水果种类hash[fruits[right]]++; // 进窗口while(kinds > 2) // 判断{hash[fruits[left]]--; // 出窗口if(hash[fruits[left]] == 0)kinds--;++left;}ret = max(ret, right - left + 1); // 更新结果++right;}return ret;
}

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

在这里插入图片描述

解法思路

  • 题意分析:题目要求找到所有的字母异位词,即找满足条件的子数组
  • 解法:滑动窗口+哈希表
    1. 先用两个哈希表将s、p中字符的出现次数分别统计
      • hash1 存储p中各个元素的出现次数
      • hash2 记录当前滑动窗口内各个元素的出现次数
    2. 进窗口:将right位置元素统计到hash2中,并记录当前有效字符的个数count
    3. 条件判断:当滑动窗口(即当前统计的子串)过大,
      • 出窗口:移动left指针,并判断有效字符
    4. 更新结果:循环尾部,每次判断(有效字符个数==p长度),则将该索引位置(left)加入到ret中

代码

vector<int> findAnagrams(string s, string p) {int hash1[26] = {0}; // hash1 存储p中各个元素的出现次数for(char ch : p)    hash1[ch - 'a']++;int count = 0, pLen = p.size(); // count 记录有效字符的个数int hash2[26] = {0}; // 记录每次滑动窗口元素出现次数vector<int> ret;for(int left = 0, right = 0; right < s.size(); ++right){char in = s[right];hash2[in - 'a']++; // 进窗口if(hash2[in - 'a'] <= hash1[in - 'a'])  count++; // 维护count,满足有效字符条件则++if(right - left + 1 > pLen) // 滑动窗口过大{char out = s[left++];if(hash2[out - 'a'] <= hash1[out - 'a']) count--;hash2[out - 'a']--;}if(count == pLen)   ret.push_back(left); // 更新结果}return ret;
}

30.串联所有单词的子串

在这里插入图片描述

解法思路

在这里插入图片描述

  • 题意分析:我们需要找到所有串联子串在s中的开始索引
  • 解法:滑动窗口+哈希表
    • 这道题与上一个题字母异位词 类似
    1. 如图所示,滑动窗口需执行len次,先嵌套一层for循环
    2. 内层循环执行滑动窗口操作↓:
      • hash1用于统计 s 中当前窗口内各个字符串出现的次数
      • hash2统计words中每个字符串的出现次数
    3. 进窗口:记录当前right位置的字符串in,如果满足条件则入hash1并更新count(有效字符串个数)
    4. 条件判断:如果窗口的大小是否超过了包含所有 words 字符串的最大可能长度
      • 出窗口:将hash中left位置字符串删去,移动left指针
    5. 更新结果:如果有效字符个数count == words大小,则将索引left加入到结果ret

代码

关于代码注释中提到的小优化:

在这里插入图片描述

// Similar to LCR 015. 找到字符串中所有字母异位词
vector<int> findSubstring(string s, vector<string>& words) {int len = words[0].size(), wSize = words.size();vector<int> ret; // 结果数组if(len > s.size())  return ret;unordered_map<string, int> hash2; // 统计 words 字符串次数for(string str : words)   hash2[str]++; // 统计次数for(int i = 0; i < len; ++i) // 执行len次{unordered_map<string, int> hash1; // 存储 s 中各字符串的次数for(int left = i, right = i, count = 0; right + len <= s.size(); right += len) // right每次跳过一个words中字符串大小// count 计算有效字符串的个数{string in = s.substr(right, len); // right位置字符串if(hash2.count(in) && ++hash1[in] <= hash2[in]) count++; // 进窗口 (hash2.count(in),小优化如果hash2中没有in,就不执行,方括号会创建变量)// 判断if(right - left + 1 > len * wSize) // 出窗口 + 维护count{string out = s.substr(left, len);if(hash2.count(out) && hash1[out] <= hash2[out]) count--;hash1[out]--;left += len;}if(count == wSize) ret.push_back(left);}}   return ret;
}

76.最小覆盖子串

在这里插入图片描述

解法思路

  • 该题与上一题串联思路相似,重点在于条件判断
  • 题意分析:题目要求返回字符串s的最小子串,该子串包含字符串t的所有字符
  • 解法:滑动窗口+哈希表
    • 先初始化相关变量和哈希表
      • hash1统计t所有字符
      • hash2记录s中当前窗口字符的出现次数
    1. 进窗口:若right位置字符满足条件,则放入hash2中,并更新count(有效字符的个数)
    2. 条件判断:根据下图,当count == kinds时:
      在这里插入图片描述
    • 更新结果:如果此时滑动窗口大小 < minLen(当前记录的最短子串长),则更新结果
    • 出窗口:将left位置字符从hash2中减去,判断是否是满足条件的字符,如果是则count–

代码

string minWindow(string s, string t) {int hash1[128] = {0}; // 统计t的字符int hash2[128] = {0}; // 统计字符串int kinds = 0; // 统计t中 字符的种类for(char ch : t) if(hash1[ch]++ == 0) kinds++;int minLen = INT_MAX, begin = -1; // 最后要用的结果,最小子串的长度和起始位置for(int left = 0, right = 0, count = 0; right < s.size(); ++right){char in = s[right];// 进窗口if(++hash2[in] == hash1[in]) count++;    // 当t中的字符次数与当前in的次数一致时,更新count// 判断while(count == kinds){if(right - left + 1 < minLen){minLen = right - left + 1; // 更新结果begin = left; // 记录待返回字符串的起始索引}char out = s[left++]; // 出窗口if(hash2[out]-- == hash1[out]) count--;}}return begin == -1 ? "" : s.substr(begin, minLen);
}

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

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

相关文章

数据分析-23--糖尿病预测(线性回归模型)(包含数据代码)

文章目录 0. 数据代码下载1. 项目介绍2. 数据处理1. 导入数据2. 处理数据 3. 建立模型4. 考察单个特征 0. 数据代码下载 关注公众号&#xff1a;『AI学习星球』 回复&#xff1a;糖尿病预测 即可获取数据下载。 算法学习、4对1辅导、论文辅导或核心期刊可以通过公众号或➕v&am…

“双十一、二” 业务高峰如何扛住?韵达快递选择 TDengine

小 T 导读&#xff1a; 为了有效处理每日亿级的数据量&#xff0c;早在 2021 年&#xff0c;韵达就选择用 TDengine 替代了 MySQL&#xff0c;并在三台服务器上成功部署和上线了 TDengine 2.0 集群。如今&#xff0c;随着 TDengine 3.0 版本的逐渐成熟&#xff0c;韵达决定将现…

android实战之添加图标到项目中

引言 阿里云矢量库有很多图标&#xff0c;注册账号&#xff0c;下载下载选择的图标&#xff0c;下载时选择svg格式。 实现 1. androidstudio &#xff0c;drawable右键---new ----->vector asset&#xff0c;选择本地下载的资源。 点击next。完成

libssh 服务端权限认证绕过(CVE-2018-10933)

漏洞描述&#xff1a; libssh 是一个提供 SSH 相关接口的开源库&#xff0c;包含服务端、客户端等。其服务端代码中存在一处逻辑错误&#xff0c;攻击者可以在认证成功前发送MSG_USERAUTH_SUCCESS消息&#xff0c;绕过认证过程&#xff0c;未授权访问目标 SSH 服务器。 复现过…

传统船检已经过时?AR智慧船检来助力!!

想象一下&#xff0c;在茫茫大海中&#xff0c;一艘巨型货轮正缓缓驶过。船上的工程师戴着一副先进的AR眼镜&#xff0c;他们不再需要反复翻阅厚重的手册&#xff0c;一切所需信息都实时显示在眼前。这不是科幻电影的场景&#xff0c;而是智慧船检技术带来的现实变革。那么问题…

[Linux]Ubuntu noVNC使用

又到了逛大型程序员交友 网站的时间了&#xff0c;今天你准备好了吗。 今天要推荐的一个有趣的项目是noVNC setup好以后是这个样子的&#xff0c;可以在浏览器登陆vnc&#xff0c;不需要再安装一个vnc client. setup的过程比较简单&#xff0c;分为以下几步&#xff1a; 1. v…

Linux操作系统——进程(六) 进程地址空间

进程地址空间 C/C程序员一般将我们所写的程序看成如下这种结构&#xff1a; 我们所写的程序通过编译编译之后就可以以这样的方式进行分布. 我们先通过编写一段C语言代码来进行验证&#xff1a; 运行结果&#xff1a; 我们可以看出来上述地址遵循的就是我们上面画的一种结构。…

059:vue中使用 AJAX来读取来自XML文件的信息

第059个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

mac 生成 本地.ssh

输入下面命令行 ssh-keygen 默认回车得到下面的 Generating public/private rsa key pair. Enter file in which to save the key (/Users/{用户名}/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has be…

Azure 学习总结

文章目录 1. Azure Function1.1 Azure Function 概念1.2 Azure Function 实现原理1.3 Azure Function 本地调试1.4 Azure Function 云部署 2. Azure API Managment 概念 以及使用2.1 Azure API 概念2.2 Azure API 基本使用 3. Service Bus 应用场景及相关特性3.1 Service Bus 基…

京东商家数据工具讲解(一):竞品数据如何监控与分析

京东平台的店铺众多&#xff0c;同行数不胜数。作为商家&#xff0c;如果连自己竞争对手的情况都不知道的话&#xff0c;很难在这个平台存活下去。那么&#xff0c;这次鲸参谋就来重点说一下“竞品分析”。 竞品分析&#xff0c;主要是对京东店铺运营期间竞争对手的市场经营状…

【docker实战】02 用docker安装mysql

本示例采用bitnami的镜像进行安装MySQL 一、镜像搜索 先搜索一下mysql有哪些镜像 [rootlocalhost ~]# docker search mysql NAME DESCRIPTION STARS OFFICIAL AUTOMATED mysql …

Android : 画布的使用 简单应用

示例图&#xff1a; MyView.java&#xff1a; package com.example.demo;import android.content.Context; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.Vi…

C++ 之LeetCode刷题记录(四)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅&#xff0c;多学多练&#xff0c;尽力而为。 先易后难&#xff0c;先刷简单的。 14. 最长公共前缀 编写一个函数来查找字符串数组中的最长公共…

建筑覆膜板和传统木板有什么不同?

在建筑领域&#xff0c;选择合适的模板材料对于确保施工质量和效率至关重要。近年来&#xff0c;建筑覆膜板因其独特的优势越来越受到青睐&#xff0c;与传统木板相比&#xff0c;它们在多个方面展现出显著的差异。 材料和制作工艺 建筑覆膜板&#xff1a;这种板材是由木材切片…

腾讯云轻量应用服务器详细介绍

腾讯云轻量应用服务器开箱即用、运维简单的轻量级云服务器&#xff0c;CPU内存带宽配置高并且价格特别优惠&#xff0c;轻量2核2G3M带宽62元一年、2核2G4M优惠价118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;756元3年、4核8G12M带宽646元15个月等&#xf…

【linux】touch的基本使用

碎碎念 刚接触linux时候的几个最基础的命令之一&#xff0c;用来创建文件。如果使用touch --help的时候会发现作者对于touch的简介&#xff1a;Update the access and modification times of each FILE to the current time.用于修改文件的访问和时间戳 带我的leader属于那种…

Linux安装GitLab教程

Linux安装GitLab教程 1、配置yum源 相当于新建一个文件&#xff0c;通过这个文件来安装gitlab vim /etc/yum.repos.d/gitlab-ce.repo 把这些配置粘进去 [gitlab-ce] nameGitlab CE Repository baseurlhttps://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el$releasever/ gp…

C++ Qt开发:SqlTableModel映射组件应用

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍SqlTableModule组件的常用方法及灵活运用。 …

编程笔记 html5cssjs 004 我的第一个页面

编程笔记 html5&css&js 004 我的第一个页面 一、基本结构二、HTML标签三、HTML元素四、HTML属性五、编写第一个网页六、使用VSCODE小结 开始编写网页&#xff0c;并且使用第一个网页成为一个母板&#xff0c;用于完成后续内容的学习。有一个基本要求&#xff0c;显示结…