【基础算法总结】滑动窗口二

news2024/11/25 16:38:37

滑动窗口二

  • 1.水果成篮
  • 2.找到字符串中所有字母异位词
  • 3.串联所有单词的子串
  • 4.最小覆盖子串

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.水果成篮

题目链接:904. 水果成篮

题目分析

在这里插入图片描述

这道题描述很多,主要意思是从左往右有一排树,给你两个篮子,每个篮子只能装一种类型水果。你可以任选从某一颗数开始摘水果,并且一棵树必须要摘一个水果。也就是说不能跳着摘水果。当你走到某棵树面前,但树上水果与你两个篮子水果类型不一样,就不能在继续了。下面就是找的两种情况。
在这里插入图片描述
根据上面的理解,这道题我们可以转化一下:
找出一个最长的子数组的长度,并且子数组中不超过两种类型的水果

算法原理

首先要想到暴力求解,两层for循环,但是这里要求一个子数组中水果不能超过两种类型,对于这种不能有重复的元素或者这道题的意思,我们可以使用哈希表,因此第一种解法出来了。

解法一:暴力求解+哈希表
定义两个指针left=0,right=0,当right走到第三种水果位置,kinds>2了,此时我们会让left往前走,然后right回溯,在重新往前走。这就是我们的想法。
在这里插入图片描述
接下来我们看看有没有优化的可能。
这里我们就不在以特例来进行讲解,因为特殊例子并不具有代表性,可能你的想法值适用于这一个例子。如果随便拿一个数组都能满足,才是正确解法!

当right此时在往前走一个位置,kind=3,就不满足情况,因此我们现在让left往前走一步。现在我们讨论有没有必要让right回溯。left往前走只有两种情况,
一 :kinds 减小 right往前走
二 :kinds 不变 right不变

因此right根本不用回溯,要么不变,要么往前走。
这不就是同向双指针!这不是就是滑动窗口的思想吗!

在这里插入图片描述
上面的分析尤为重要,滑动窗口就那几步代码很简单,但是难的是你想到使用滑动窗口,这个why?很重要!

解法二:滑动窗口+哈希表

  1. left=0,right=0
  2. 进窗口
  3. 判断
    出窗口
  4. 更新结果

当你知道用滑动窗口了,就可以使用特例来看看怎么写代码。

当出窗口一定要注意,因为进入这个hash的这种类型的树可不止一颗,如果我们用的是这种hash只统计类型,那出窗口减去这种类型树的时候时候,明明数组里面还有这种类型的树,但是一下就减没了,是有问题的,因此hash里面不光有这种类型,还要有这里类型树的个数。

进窗口然后判断,当判断后窗口里面一定是符号条件的,然后再更新结果。
在这里插入图片描述

class Solution {
public:
    int totalFruit(vector<int>& fruits) {

        unordered_map<int,int> mp;
        int len=0;
        for(int left=0,right=0;right<fruits.size();++right)
        {
        	
            mp[fruits[right]]++;//进窗口
            while(mp.size()>2)//判断
            {
            	//出窗口
                mp[fruits[left]]--;
                if (mp[fruits[left]] == 0)
                    mp.erase(fruits[left]);
                ++left; 
            }
            //更新结果
            len=max(len,right-left+1);
        }
        return len;

    }
};

虽然代码可以通过,时间有点久,这是因为我们使用了容器导致的,对于数据范围有限,可以不使用容器,这样会提高时间效率。

在这里插入图片描述

class Solution {
public:
    int totalFruit(vector<int>& fruits) {

        //unordered_map<int,int> mp;
        int hash[100001] = {0};
        int len=0,kinds=0;
        for(int left=0,right=0;right<fruits.size();++right)
        {
            if(hash[fruits[right]] == 0)
                kinds++;
            //进窗口 
            hash[fruits[right]]++;
            //判断
            while(kinds>2)
            {
                //出窗口
                hash[fruits[left]]--;
                if (hash[fruits[left]] == 0)
                    --kinds;
                ++left; 
            }
            //更新结果
            len=max(len,right-left+1);
        }
        return len;

    }
};

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

题目链接:438. 找到字符串中所有字母异位词

题目分析

在这里插入图片描述

异位词:相同字母重新排序形成的字符串
在这里插入图片描述
在解决这道题前面,我们首先想如何快速判断两个字符串是否是 “异位词”
在这里插入图片描述
首先肯定会想把两个字符串排序一下,然后再每个字依次对比。这样是肯定能解决问题的,时间复杂度O(n+nlog^n)

还有其他更好的方法吗?
以前说过对于找重复的元素,什么最快?
肯定是哈希表
因此我们直接用哈希表就可以了。
在这里插入图片描述

算法原理

解法一:暴力求解+哈希表
我们可以把所有字符串中所有字串都找到结合哈希表,肯定能得到正确结果,但是肯定通过不了,时间复杂度太高了。
在这里插入图片描述

接下来想想有没有优化的可能
定义left,right指针,当right走到>len的位置,就停下来,因为超过p的长度了,根本就不可能是异位词了。然后left往前走一步,注意每次left只用走一步就可以了。因为此时left到right区间长度刚好等于len,所有left每次就走一步。
在这里插入图片描述

然后right也不用回溯,因为元素就已经在hash1中。并且right每次也就往后走一步就行了。走多了就不满足了。
在这里插入图片描述

到这里我们肯定直到是要用滑动窗口了,但是这里的滑动窗口和之前的不一样。

滑动窗口的分类:

1.非固定大小滑动窗口
利用单调性,规律,每次进窗口出窗口可能会好多次。判断出窗口是一个循环。

2.固定大小滑动窗口
满足特定条件,每次进窗口出窗口一次。判断出窗口就执行一次。

解法二:滑动窗口+哈希表

在这里插入图片描述

class Solution {
public:
    bool check(int* hs,int* hp)
    {
        for(int i=0;i<26;++i)
        {
            if(hs[i] != hp[i])
                return false;
        }
        return true;
    }

    vector<int> findAnagrams(string s, string p) 
    {
         vector<int> ret;
         int hashs[26]={0},hashp[26]={0};
         for(auto& ch:p)
             hashp[ch-'a']++;

         int len=p.size();
         for(int left=0,right=0;right<s.size();++right)
         {
             hashs[s[right]-'a']++;
             if(right-left+1>len)
             {
                 hashs[s[left]-'a']--;
                 ++left;
             }

             if(check(hashs,hashp))
                 ret.push_back(left);

         }
         return ret;
     }
}

这道题给的是小写字母,因此hash大小设为26。然后每次判断后都要check内部循环检查一下两个哈希表是否相等,因此这个时间复杂度是O(26*N)最终是O(N),对于这道题来说没问题,但是给你换成字符串就有问题了。

所以在优化一下:
优化:更新结果的判断条件
利用变量 count 来统计窗口中 “有效字符” 的个数

目前p中字符共3个,其中字符a、b、c分别占一个。那如果保证滑动窗口内有效字符(a、b、c)个数等于len,并且与p中字符a、b、c个数一一对应。那就能直接说明该滑动窗口是满足条件的,此时只要把left下标添加道返回结果就行了。这样就不用在每次循环判断两个hash表了。
在这里插入图片描述
当right第一次指向c,count+1,但是当right往后走再次指向c,就不要跟新count了,因为p中c字符就才1个!
在这里插入图片描述
当right到b的时候,count+1。此时判断一下,有效字符count个数为2,len=3,不相等。如果count个数正好等于3说明窗口里面全都是有效字符。
所以我们可以做到一边在滑动窗口里面添加元素,一边维护count,同时我们还可以知道什么时候是字母异位词。
在这里插入图片描述
然后right再走遇到a,a变成1,然后更新count+1。但是此时这个窗口大小已经超过len,因此先要出窗口left往后移一步。滑动窗口内字符c个数要减1,但是删掉之前count需不需要更新呢?并不需要,因为c=2,比p中c=1多一个字符,那删掉的c就是多余字符,因此并不需要更新count。在判断一下此时有效字符count==len,说明窗口里是合法字符,此时把left添加到返回数组就可以了。

在这里插入图片描述
总结:
进窗口: 进窗口后,hash2[in] <= hash1[in] —> count++
出窗口: 出窗口前,has2[out] <= hash1[out] —> count–
更新结果:count==len

class Solution {
public:

    vector<int> findAnagrams(string s, string p) {

        vector<int> ret;
        int hash1[26]={0};//统计字符串 p 中每个字符出现的个数

        for(auto& ch:p) hash1[ch-'a']++;

        int hash2[26]={0};//统计窗口里面的每一个字符出现的个数

        int len=p.size(),count=0;
        for(int left=0,right=0;right<s.size();++right)
        {
            char in=s[right];
            //进窗口 + 维护 count
            if(++hash2[in -'a'] <= hash1[in -'a']) 
                count++;

            if(right-left+1>len)//判断
            {
                //出窗口 + 维护 count
                char out=s[left++];
                if(hash2[out -'a']-- <= hash1[out-'a'])
                    count--;

            }

            if(count == len)
                ret.push_back(left);

        }
        return ret;
    }
}      

3.串联所有单词的子串

题目链接:30. 串联所有单词的子串
题目分析

在这里插入图片描述

注意这道题很重要一点就是 words 中所有字符串 长度相同
其次这道题的意思是让在s字符串中,找到words数组中字符串的任意组合。找到后返回下标。

在这里插入图片描述

算法原理
其实这道题和上面的 438. 找到字符串中所有字母异位词 ,解法思路一模一样,为什么这样说呢。因为上面这道题让找的是字母,这道题增加难道让找的是字符串的异位串!

words 中所有字符串 长度相同,我们设它为len长,然后把s中len个字符组合在一起,这不就是438. 找到字符串中所有字母异位词的题吗!

在这里插入图片描述

解法一:暴力求解+哈希表

不过有三处地方和上面不一样,需要注意,其他方面都是一样的解题思路

  1. 因为这里是字符串,因此我们使用unordered_map<string,int>容器,string用来记录字符串,int用来记录每个字符串出现的频次
  2. left 与 right 指针的移动
    left 和 right每次移动的步长,是单词的长度 ----> len
    在这里插入图片描述
  3. 因为我们指针是跳着走的,我们有的情况没有找完,因此滑动窗口执行的次数要len次

在这里插入图片描述

其他思路是完全一模一样的。

class Solution {
public:

    vector<int> findSubstring(string s, vector<string> words) {

        vector<int> ret;

        unordered_map<string,int> hash1;//保存 words 里面所有字符串频次
        for (auto& str : words) hash1[str]++;

        int len=words[0].size(),m=words.size();
        for(int i=0;i<len;++i)//执行len次滑动窗口
        {
            unordered_map<string,int> hash2;//维护窗口内字符串
            //小心right往后走截取字符串越界问题!
            for(int left=i,right=i,count=0;right+len<=s.size();right+=len)
            {
                // 进窗口 + 维护 count
                string in=s.substr(right,len);
                hash2[in]++;
                if(hash1.count(in) && hash2[in] <= hash1[in]) 
                    ++count;

                //判断
                if(right-left+1>len*m)
                {
                    //出窗口 + 维护 count
                    string out=s.substr(left,len);
                    if(hash1.count(out) && hash2[out] <= hash1[out])
                        --count;
                    hash2[out]--;
                    left+=len;
                }
                //更新结果
                if(count == m)
                    ret.push_back(left);
            }
        }
        return ret;

4.最小覆盖子串

题目链接:76. 最小覆盖子串

题目分析

在这里插入图片描述
本题是让在一个字符串找到包含另一个字符串的最短字串,其中我们看它的注意提醒,对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。这句话有点抽象,我们画图说明一下

注意看这个画圈的地方,这也是符合条件的字串,虽然B有两个字符。也就是说在s中找到符合要求的子字符串中对应字符可以大于的t字符串中的字符,这种字符串是满足条件的!

在这里插入图片描述

算法原理

我们首先会想到暴力枚举把所有符合条件的子字符串找出来,一定可以找到其中最小的。那怎么找到符合条件的子字符串呢?我们这里还是借助哈希表。

解法一:暴力枚举+哈希表
在这里插入图片描述
下面看看有没有优化的可能,我们优化时暂时不用特例,搞一个任何情况下都满足的情况,现在left和right区间就是满足条件的最短子字符串。暴力枚举就是left+1,right回溯,然后重新往后找。

在这里插入图片描述
现在看看right有没有必要回溯在重新找。当left+1,可能有两种情况:
1.字符串不符合要求了
2.字符串依然符合要求

字符串不符合要求了那就是变短了,right如果回溯然后重新走到老位置,不还是不符合要求吗?此时right不需要回溯,右移往后走就行了

字符串依然符合要求,那right回溯之后还是走到老位置不还是符合要求吗?此时right不动就行了

在这里插入图片描述
两个指针,同向! -----> 滑动窗口!

解法二:滑动窗口+哈希表

滑动窗口就那几步,我们这时可以使用特例来确实什么时候进窗口,判断,出窗口,更新结果

在这里插入图片描述
每次判断都要遍历数组,太麻烦了。我们把判断优化一下。
优化: 判断条件
使用变量 count 标记有效字符的种类
如果还像之前统计有效字符个数就有问题了,虽然有效字符个数相等但是并不是字串!
在这里插入图片描述

优化:
一定是字符个数相等才能说是种类一样!

进窗口后当 hash2[in] == hash1[in] count++

出窗口前当 hash2[out] == hash1[out] count- -

判断 count == hash1.size() 滑动窗口内字符种类和hash表中字符种类相等

class Solution {
public:

    string minWindow(string s, string t) {
        int hash1[128]={0};// 统计字符串 t 中每一个字符出现的频次
        int kinds=0;// 统计有效字符有多少种
        for(auto& ch : t) 
        {
            if(hash1[ch]++ == 0)
                ++kinds;
        }        

        int hash2[128]={0};// 统计窗口内每个字符的频次
        int begin=-1,minlen=INT_MAX;
        for(int left=0,right=0,count=0;right<s.size();++right)
        {
            //进窗口 + 维护count
            char in=s[right];
            if(++hash2[in] == hash1[in])
                ++count;
            
            //判断
            while(count == kinds)
            {
                //更新结果
                if(right-left+1 < minlen)
                {
                    minlen=right-left+1;
                    begin=left;
                }

                //出窗口 + 维护 count
                char out=s[left++]; 
                if(hash2[out]-- == hash1[out])
                    --count;

            }
        }

        if(begin == -1) return "";
        else return s.substr(begin,minlen);

    }
};

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

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

相关文章

Dbeaver network unavailable due to certificate issue

场景&#xff1a;出现在DBeaver连接数据库下载驱动的时候 解决&#xff1a; 别勾选就可以了

Java的基本语法

文章目录 Java语言的一些基本语法要点&#xff1a;Java语言的一些进阶概念和特性&#xff1a;Java代码示例基础示例1. Hello World2. 计算两个数的和 进阶示例1. 使用Lambda表达式过滤列表2. 实现一个简单的泛型类3. 使用多线程打印数字 异常处理示例捕获并处理异常 接口使用示…

【前端】HTML实现个人简历信息填写页面

文章目录 前言一、综合案例&#xff1a;个人简历信息填写页面 前言 这篇博客仅仅是对HTML的基本结构进行了一些说明&#xff0c;关于HTML的更多讲解以及CSS、Javascript部分的讲解可以关注一下下面的专栏&#xff0c;会持续更新的。 链接&#xff1a; Web前端学习专栏 下面我对…

Python专题:六、循环语句(2)

for循环语句 列表可以简单的理解为&#xff1a; 顺序保存的若干元素 注释&#xff1a;变量largest&#xff0c;循环语句for&#xff0c;还有二层缩进八个空格 依次取出counts&#xff08;列表&#xff09;里的数字&#xff0c;并赋予给x&#xff0c;判断x和largest数值大小。…

深度学习之视觉特征提取器——AlexNet

AlexNet 参考资料&#xff1a; &#xff08;1&#xff09;ImageNet十年历任霸主之AlexNet - 知乎 (zhihu.com) &#xff08;2&#xff09;AlexNet - Wikipedia 引入 AlexNet在2012年以第一名在Top-1分类精度霸榜ImageNet&#xff0c;并超过第二名近10个百分点&#xff0c;…

OpenCV-基于累计直方图的中值滤波算法

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 实现原理 基于累计直方图的中值滤波算法是一种图像处理技术&#xff0c;用于去除图像中的噪声。它利用了像素值的频数分布&#…

图片过大怎么处理变小?在线编辑图片工具推荐

在各种平台进行图片上传时&#xff0c;经常会遇到由于图片过大而无法成功上传的问题&#xff0c;为了顺利进行下一步操作&#xff0c;我们需要将图片进行缩小处理&#xff0c;通常情况下&#xff0c;我们可以使用各种软件工具来对图片进行缩小&#xff0c;如何快速有效地调整图…

自动驾驶系统中的端到端学习

资料下载-《自动驾驶系统中的端到端学习&#xff08;2020&#xff09;》https://mp.weixin.qq.com/s/ttNpsn7qyVWvDMZzluU_pA 近年来&#xff0c;卷积神经网络显著提高了视觉感知能力。实现这一成功的两个主要因素是将简单的模块组合成复杂的网络和端到端的优化。然而&#xf…

源代码防泄密-文档加密与沙盒加密的区别

研发人员比普通办公人员要精通电脑&#xff0c;除了常见的网络&#xff0c;邮件&#xff0c;U盘&#xff0c;QQ等数据扩散方法外&#xff0c;外设中转对研发人员来说轻而易举&#xff1a; — 对于嵌入式开发场景&#xff0c;可以通过串口&#xff0c;U口&#xff0c;网口把代码…

美团二面:SpringBoot读取配置优先级顺序是什么?

引言 Spring Boot作为一种轻量级的Java应用程序框架&#xff0c;以其开箱即用、快速搭建新项目的特性赢得了广大开发者的青睐。其核心理念之一就是简化配置过程&#xff0c;使开发者能够快速响应复杂多变的生产环境需求。为了实现这一点&#xff0c;Spring Boot支持丰富的外部…

OpenVoiceV2本地部署教程,苹果MacOs部署流程,声音响度统一,文字转语音,TTS

最近OpenVoice项目更新了V2版本&#xff0c;新的模型对于中文推理更加友好&#xff0c;音色也得到了一定的提升&#xff0c;本次分享一下如何在苹果的MacOs系统中本地部署OpenVoice的V2版本。 首先下载OpenVoiceV2的压缩包&#xff1a; OpenVoiceV2-for-mac代码和模型 https:…

Minio(官方docker版)容器部署时区问题研究记录

文章目录 感慨&概述补充&#xff1a;MINIO_REGION和容器时间的关系 问题一&#xff1a;minio容器和本地容器时间不一致问题说明原因探究解决方法结果验证 问题二&#xff1a;minio修改时间和本地查询结果不一致具体问题原因探究解决办法时间转化工具类调用测试和验证上传文…

4. 从感知机到神经网络

目录 1. 从感知机到神经网络 2. 最简单的神经网络 3. 激活函数的引入 1. 从感知机到神经网络 之前章节我们了解了感知机&#xff0c;感知机可以处理与门、非与门、或门、异或门等逻辑运算&#xff1b;不过在感知机中设定权重的工作是由人工来做的&#xff0c;而设定合适的&a…

障碍物识别软件的优缺点

在这个科技与人文关怀交织的时代&#xff0c;一款基于激光雷达技术的障碍物识别软件正悄然为视障人士的日常生活带来一场革命性的改变。这一款叫做“蝙蝠避障”的软件利用先进科技的力量&#xff0c;为盲人出行铺设了一条更加安全、独立的道路。今天&#xff0c;让我们从资深记…

智能呼叫中心客服系统:企业客户服务的新引擎

在如今商业竞争激烈的大环境下&#xff0c;企业的客户服务需求已不仅仅局限于简单的沟通。随着科技的进步&#xff0c;客户对于高效、智能的交互体验有着更高的期待。为了满足这些需求&#xff0c;智能呼叫中心客服系统应运而生&#xff0c;成为企业提升客户服务质量、优化客户…

vs Code配置python环境

vs Code 作为一款轻量级的源代码编辑IDE,其以轻量级和易用性著称&#xff0c;笔者今天记录分享下在其上面配置python环境&#xff0c;因为 笔者最近在学习了解Python&#xff0c;其实关于python的IDE有 pyCharm,但后期还需要进行其他语言的开发&#xff0c;索性就弄个集成度比较…

Python专题:十、字典(1)

数据类型:字典,是一个集合性质的数据类型 字典的初始化 字典{关键字:数值} 新增元素 修改元素 字典元素访问 字典[关键字} in 操作符 字典关键字检测 字典元素遍历 ①遍历关键字

每日OJ题_记忆化搜索②_力扣62. 不同路径(三种解法)

目录 力扣62. 不同路径 解析代码1_暴搜递归&#xff08;超时&#xff09; 解析代码2_记忆化搜索 解析代码3_动态规划 力扣62. 不同路径 62. 不同路径 难度 中等 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器…

【算法基础实验】排序-最小优先队列MinPQ

优先队列 理论知识 MinPQ&#xff08;最小优先队列&#xff09;是一种常见的数据结构&#xff0c;用于有效管理一组元素&#xff0c;其中最小元素可以快速被检索和删除。这种数据结构广泛应用于各种算法中&#xff0c;包括图算法&#xff08;如 Dijkstra 的最短路径算法和 Pr…

弱监督语义分割-对CAM的生成过程进行改进1

一、仿射变换图像结合正则项优化CAM生成 论文&#xff1a;Self-supervised Equivariant Attention Mechanism for Weakly Supervised Semantic Segmentation &#xff08;CVPR,2020&#xff09; 1.SEAM方法 孪生网络架构&#xff08;Siamese Network Architecture&#xff09…