算法通过村第十六关-滑动窗口|白银笔记|经典题目讲解

news2024/9/22 15:34:40

文章目录

  • 前言
  • 最长字串专场
    • 无重复字符的最长字串
    • 至多包含两个不同字串的最长子串
    • 至多包含K个不同字串的最长子串
  • 长度最小的子数组
  • 盛水最多的容器
  • 寻找字串异位词(排序)
    • 字符串的排序
    • 找到字符串中所有字母异位
  • 总结


前言


提示:所有的话语都颇为类似,而沉默则千差万别。 --卢梭

趁热打铁,我们继续来研究一些高频、热门的滑动窗口问题。

最长字串专场

先看最高频的推荐题目⭐⭐⭐⭐:

3. 无重复字符的最长子串 - 力扣(LeetCode)

滑动窗口—至多包含两个不同字符的最长子串(leetcode 159)_珠穆拉玛峰的博客-CSDN博客

LeetCode算法日记:340.至多包含K个不同字符的最长子串_算法 至多-CSDN博客

看这题目,我们要怎么处理呢?这种造题是不是自己拿手的,顺着这个思路是不是可以造出很多题目来,总结出一些方法,解滑动窗口的模板呢?

无重复字符的最长字串

参考题目地址:3. 无重复字符的最长子串 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述

要找到最长的子串,必然要知道无重复字符串的首部和尾部,然后再从中确定最长的那个,因此至少需要两个指针才可以,这也是滑动窗口思想。即使用滑动窗口,深入分析会发现,具体处理来由很多方式。这里介绍一种经典的使用Map的思考。

我们这里定义一个K—V形式的map,key表示的是当前正在访问的字符串,value是器下标索引值。我们每访问一个新元素,都将其下标更新成对应的索引值。

在这里插入图片描述
如果已经出现过,就和上图的做法一致,abcabc,当第二次出现a的时候,我们就更新left成为第一个b的所在位置,此时惊奇的事情发生了,left要移动的位置恰好就是map.get(‘a’)+1= 1,我们将‘a’用序列来表示,放在一起就是map.get(s.charAt(i)) + 1。其他情况可以类似考虑,参考上图见解。

当然这里有一种特殊的情况,例如abba,我们再次访问b时,left = map.get(‘b’) + 1 = 2。然后继续访问第二个a,这时 left = map.get(‘a’) + 1 = 1,也就是left要后推的节奏,显然就不对了。所以这里要调整逻辑,保留最大值。

这样就可以了

left = Math.max(left,map.get(s.charAt(i)) + 1);

完整代码如下:

/**
     * 最长无重复字串
     * @param s
     * @return
     */
    public static int lengthOfLongestSubstring(String s) {
       if (s == null || s.length() == 0){
           return 0;
       }
        Map<Character,Integer> map = new HashMap<>();
       int max = 0;
       int left = 0;
       for(int right = 0; right < s.length(); right++){
           if (map.containsKey(s.charAt(right))){
               // 确保left一直向前走 不回头
               left = Math.max(left,map.get(s.charAt(right)) + 1);
           }
           map.put(s.charAt(right),right);
           max = Math.max(max,right - left + 1);
       }
       return max;

    }

除了上述方法,不用Hash存储索引,也是可以用滑动窗口的思想来解决的,感兴趣的是试一试。

至多包含两个不同字串的最长子串

根据上题目的变种,试想以下,这里如果求包含两个不同字串的最长子串呢。

你需要考虑两个问题:

  • 怎么判断是两个
  • 什么删除元素

如果还使用上述方法是否行的通,left的值应该怎么求舍?

要判断只有两个元素,当然还是Hash好用一些,每一个时刻,判断HashMap中不得超过3个元素。这里就要解决下一个问题,怎么移除了。

还是采用key-value的含义,这次将字符串里的字符都当做键,在窗口中的最右边的字符位置作为值。我们在看下伪代码是删除的逻辑:

del_index = Collections.min(hashMap.values());
left = del_index + 1;

还是看图更直接一些:
我们可以充分利用Map来解决这个问题:

 /**
     * 至少包含两个字符的最长子串
     * @param s
     * @return
     */
    public static int lengthOfLongestSubstringTwoDistinct(String s) {
        if (s.length() < 3){
            return s.length();
        }

        int left = 0;
        int right = 0;
        HashMap<Character,Integer> map = new HashMap<>();
        int maxLen = 2;
        while(left < s.length()){
            if (map.size() < 3){
                map.put(s.charAt(right),right++);
            }
            // 如果大于3就靠考虑了
            if (map.size() == 3){
                // 删除最左侧的位置
                int del_idx = Collections.min(map.values());
                map.remove(s.charAt(del_idx));
                // 更新窗口的位置
                left = del_idx + 1;
            }
            maxLen = Math.max(maxLen, right - left);
        }
        return maxLen;
    }

至多包含K个不同字串的最长子串

这更改2的位置就可以了,正如上个题目一样:

只要将判断HashMap大小为2改成k就可以了,超过2就是k+ 1,实现不就很简单的事情。

    /**
     * 至多包含k个字符的最长子串
     *
     * @param s
     * @param k
     * @return
     */
    public static int lengthOfLongestSubstringKDistinct(String s, int k) {
        if (s.length() < k + 1) {
            return s.length();
        }

        int left = 0, right = 0;
        HashMap<Character, Integer> hashmap = new HashMap<>();
        int maxLen = k;

        while (right < s.length()) {

            if (hashmap.size() < k + 1) {
                hashmap.put(s.charAt(right), right++);
            }
            // 如果大小达到了k个
            if (hashmap.size() == k + 1) {
                //
                int del_idx = Collections.min(hashmap.values());
                hashmap.remove(s.charAt(del_idx));
                // 窗口left的新位置
                left = del_idx + 1;
            }

            maxLen = Math.max(maxLen, right - left);
        }
        return maxLen;
    }

长度最小的子数组

参考题目介绍:209. 长度最小的子数组 - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
本题目可以使用双指针来解决,可以视为队列法。基本思路是先让元素不断入队,当入队元素和等于target时就记录以下此时队列的容量,如果队列元素之和大于target则开始出队,知道小于target则再入队。

当然如果等于target的情况,则记录以下此时队列的大小,之后继续先入队再出队。每当出现元素之和等于target时我们就保留容量最小的那个。

 /**
     * 长度最小的子数组
     * @param target 目标值
     * @param nums  数组
     * @return 返回大小
     */
    public static int minSubArrayLen(int target, int[] nums) {

        // 初始化变量
        int left = 0, right = 0,sum = 0,min = Integer.MAX_VALUE;
        while(right < nums.length){
            sum += nums[right++];
            while(sum >= target){
                min = Math.min(min,right - left);
                sum -= nums[left++];
            }
        }
        return min == Integer.MAX_VALUE ? 0 : min;
    }

盛水最多的容器

参考地址:11. 盛最多水的容器 - 力扣(LeetCode)
在这里插入图片描述
这个题目看似很复杂,但是换个思路很简单的,假设两个指针i,j。指向的水槽板高度分别为h[i],h[j],此时状态下水槽面积为s(i,j)。由于可容纳水的高度有两个板中的腐短板决定的,因此可以得出面积公式:

s(i,j) = min(h[i],h[j])*(j - i)

那么我们就要考虑以下问题:

每个状态,不论是长板还是短板向中间收缩一格,都会导致水槽边宽度-1变短:

  • 若向内移动短板,水槽得短板min(h[i],h[j])可能变大,因此下一个水槽得面积可能会增加
  • 若向内移动长板,水槽得短板min(h[i],h[j])不变或者变小,因此下一个水槽得面积一定是变小的

因此,这里初始化双指针分别位于水槽左右两端,循环每轮移动向内移动一格,并更新面积最大值,知道两个指针相遇时跳出;即可获得最大面积。

	/**
     * 盛最多水的容器
     * @param height the height of the array
     * @return
     */
    public static int maxArea(int[] height) {
        int i = 0, j = height.length,res = 0;
        while(i < j){
            // 那最小向中间移动
            res = height[i] < height[j] ?
                    Math.max(res,(j - i)* height[i++]):
                    Math.max(res,(j - i)* height[j++]);
        }
        return res;
    }

寻找字串异位词(排序)

如果两个字符串仅仅时字母出现的位置不一样,则称两者互相为对方的一个排列,也常称为异位词。

这里推荐两个题目⭐⭐⭐⭐:

567. 字符串的排列 - 力扣(LeetCode)

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

字符串的排序

参考题目地址:567. 字符串的排列 - 力扣(LeetCode)

在这里插入图片描述
本题最简单的方式就是用s1做窗口,依次比较得出结果,但是这样的效率太低,我们看看有没有更好更优的办法。

所谓异位词要抓住两点:

  • 字母类型一样
  • 出现个数相同

因此,我们可以创建一个为26的数组,每个位置对应a-z的个数,为了方便操作,索引做了调整,

index = s1.charAt(i) - 'a';

这个是字符串常用的技巧。

此时窗口一直向前移动就行了。

charArray[s2.charAt(right) - 'a']++

而left向右移动就执行

int left = right - sLen1;
charArray2[s2.charAt(left) - 'a']--;

完整代码是下面的:

  /**
     * 字符串的排列
     * @param s1
     * @param s2
     * @return
     */
    public static boolean checkInclusion(String s1, String s2) {
        // 参数检验
      int len1 = s1.length(),len2 = s2.length();
      if (len1 > len2){
          return false;
      }

        int[] chars1 = new int[26];
        int[] chars2 = new int[26];
        // 先读len1
        for (int i = 0; i < len1; i++){
            chars1[s1.charAt(i) - 'a']++;
            chars2[s1.charAt(i) - 'a']++;
        }

        if (Arrays.equals(chars1, chars2)){
            return true;
        }
        // 注意维护窗口变化
        for (int right = len1; right < len2; right++){
            chars2[s2.charAt(right) - 'a']++;
            int left = right - len1;
            chars2[s2.charAt(left) - 'a']--;
            if (Arrays.equals(chars1, chars2)){
                return true;
            }
        }
        return false;
    }

上面只是判断有没有,那么如果让你确定右几个呢?有或者如果存在的话,将异位词的开始位置和结束位置输出出来怎么样?那就看看下一题吧

找到字符串中所有字母异位

参考题目介绍:438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

在这里插入图片描述
思路可以说是一摸一样,简单改下代码就可以了,唯一不同的是,采用List存放,出现异位词的,记录以下开始位置,那么可以直接将其加入到list中。

开下完整的代码:

    /**
     * 找到字符串中所有字母异位词
     * @param s1
     * @param s2
     * @return
     */
    public static List<Integer> findAnagrams(String s1, String s2) {
        // 参数检验
        int len1 = s1.length(),len2 = s2.length();
        if (len1 > len2){
            return new ArrayList<Integer>();
        }

        int[] chars1 = new int[26];
        int[] chars2 = new int[26];
        // 先读len1
        for (int i = 0; i < len1; i++){
            chars1[s1.charAt(i) - 'a']++;
            chars2[s1.charAt(i) - 'a']++;
        }
        ArrayList<Integer> res = new ArrayList<>();

        if (Arrays.equals(chars1, chars2)){
            res.add(0);
        }
        // 注意维护窗口变化
        for (int left = 0; left < len1 - len2; left++ ){
            chars2[s1.charAt(left) - 'a']--;
            int right = left + len1;
            chars2[s1.charAt(right) - 'a']++;
            if (Arrays.equals(chars1, chars2)){
            	// 拿到最最左位置
                res.add(left + 1);
            }
        }
        return res;
    }

总结

提示:滑动窗口问题;双指针问题;字符串变种;HashMap的特殊存储;模板套路


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。

在这里插入图片描述

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

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

相关文章

视频号视频提取工具,操作简单!一键搞定

在当下信息爆炸的时代&#xff0c;视频成为了人们获取信息、娱乐和交流的重要方式。而随着视频创作的普及&#xff0c;越来越多的人希望能够从各类视频中提取出有价值的素材和片段&#xff0c;以便用于自己的创作需求。然而&#xff0c;对于大多数人来说&#xff0c;费时费力地…

极智项目 | 实战静默活体人脸检测

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多经验分享 大家好&#xff0c;我是极智视界&#xff0c;本文来介绍 实战静默活体人脸检测。 本文介绍的 实战静默活体人脸检测&#xff0c;提供完整的可以一键执行的项目工程源码&#xff0c;获取方式有两个&#xff1a…

云音乐Android Cronet接入实践

背景 网易云音乐产品线终端类型广泛&#xff0c;除了移动端&#xff08;IOS/安卓&#xff09;之外&#xff0c;还有PC、MAC、Iot多终端等等。移动端由于上线时间早&#xff0c;用户基数大&#xff0c;沉淀了一些端侧相对比较稳定的网络策略和网络基础能力。然而由于各端在基础…

数字化营销如何推动企业营收增长?数字化营销要点有哪些?

在数字化席卷而来的时代&#xff0c;企业若想在激烈的市场竞争中脱颖而出&#xff0c;就得紧跟潮流&#xff0c;运用数字化营销手段更快、更准地触达目标客户&#xff0c;从而帮助企业更好地解读客户需求&#xff0c;捕捉痛点&#xff0c;实现精细化营销闭环。 数字化营销如何让…

信奥赛一本通:数据排序(合影效果、病人排队、明明的随机数、单词排序、出现次数超过一半的数、统计字符数)

数据排序 1182&#xff1a;合影效果1183&#xff1a;病人排队1184&#xff1a;明明的随机数1185&#xff1a;单词排序1186&#xff1a;出现次数超过一半的数1187&#xff1a;统计字符数 1182&#xff1a;合影效果 由题目可知&#xff0c;n个人有 性别与身高两种属性&#xff0c…

java连接mysql数据库结构表批量生产word文档

java连接数据库&#xff0c;数据库结构表批量生产word文档 pom包引用 <dependency><groupId>cn.smallbun.screw</groupId><artifactId>screw-core</artifactId><version>1.0.5</version> </dependency><dependency>&l…

一句话解释什么是出口IP

出口 IP 是指从本地网络连接到公共互联网时所使用的 IP 地址。这个 IP 地址是由 Internet 服务提供商(ISP)分配给你的,它可以用来标识你的网络流量的来源。如果你使用的是 NAT(网络地址转换)技术,则在 NAT 设备内部会进行地址转换,使得多个设备可以共享同一个公共 IP 地…

功能不够,SQL来凑,修改数据库的正确姿势?

修改数据库是一项关键任务&#xff0c;需要小心谨慎地执行&#xff0c;以确保数据的完整性和准确性。下面是一个详细的步骤指南&#xff0c;介绍了正确修改数据库的姿势。 第一步&#xff1a;备份数据库 在进行任何数据库修改之前&#xff0c;务必备份数据库。这样&#xff0…

TOUGH模型教程

详情点击公众号链接&#xff1a;全流程TOUGH系列软件 一、 第一&#xff1a;多相流流体基本特征及TOUGH系列软件 1.1多相流流体基本特征与解决思路 1.2 TOUGH2系列软件 1.3 TOUGH2软件功能模块 1.4 TOUGH2软件设计思路 1.5 TOUGH2软件数学模型与数值方法 二、基础 第二&…

HarmonyOS 音频开发指导:使用 OpenSL ES 开发音频播放功能

OpenSL ES 全称为 Open Sound Library for Embedded Systems&#xff0c;是一个嵌入式、跨平台、免费的音频处理库。为嵌入式移动多媒体设备上的应用开发者提供标准化、高性能、低延迟的 API。HarmonyOS 的 Native API 基于Khronos Group开发的OpenSL ES 1.0.1 API 规范实现&am…

全志R128基础组件开发指南——SPI LCD 显示驱动

SPI LCD 显示驱动 简介 R128 平台提供了 SPI DBI 的 SPI TFT 接口ACCC&#xff0c;具有如下特点&#xff1a; Supports DBI Type C 3 Line/4 Line Interface ModeSupports 2 Data Lane Interface ModeSupports data source from CPU or DMASupports RGB111/444/565/666/888 …

买学生台灯主要看什么?双十一值得入手的学生台灯

对于台灯来说主要是光源的问题。随着科技时代发展&#xff0c;那种原有的白炽灯&#xff0c;钨丝灯早已不见&#xff0c;换言之是更多的LED灯&#xff0c;再加之它体积小便于安装&#xff0c;而且光线更加均匀柔和&#xff0c;所以深受用户的青睐。 而台灯又是孩子学习必不可少…

2023年【汽车驾驶员(中级)】报名考试及汽车驾驶员(中级)证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 汽车驾驶员&#xff08;中级&#xff09;报名考试根据新汽车驾驶员&#xff08;中级&#xff09;考试大纲要求&#xff0c;安全生产模拟考试一点通将汽车驾驶员&#xff08;中级&#xff09;模拟考试试题进行汇编&…

Shopee买家通系统全自动化操作简单方便又快速

Shopee买家通系统是一款专门针对虾皮买家号所开发的全自动化操作系统&#xff0c;可以自动注册、自动加购加心愿单、自动下单等。 1、全自动化注册 准备好账号需要的资料后即可运行注册任务&#xff0c;程序运行时可以自动输入手机号、自动接收短信、自动输入账号密码。账号支…

浙大陈越何钦铭数据结构06-图1 列出连通集

题目 给定一个有N个顶点和E条边的无向图&#xff0c;请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时&#xff0c;假设我们总是从编号最小的顶点出发&#xff0c;按编号递增的顺序访问邻接点。 输入格式: 输入第1行给出2个整数N(0<N≤10)和E&…

2023年9月青少年机器人技术(四级)等级考试试卷-理论综合

2023.9青少年机器人技术等级考试理论综合试卷&#xff08;四级&#xff09; 分数&#xff1a;100 题数&#xff1a;30 一、单选题(共20题&#xff0c;共80分) 1. Arduino C程序如下&#xff0c;当程序运行时&#xff0c;串口监视器输出结果是&#xff1f;&#xff08; &…

微信视频号怎么下载视频?

微信视频号视频和其他平台有个最大的区别就是&#xff0c;没有链接&#xff0c;其他的下载器无法解析&#xff0c;知道我发现了一款非常适用于微信的下载工具&#xff0c;只需要将视频转发下载机器人就能一键解析成功&#xff0c;这么听着有点不懂对不对&#xff1f;别急&#…

【C++代码】回溯,子集,组合,全排列,去重--代码随想录

题目&#xff1a;分割回文串 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。 在for (int i startIndex; i < s.size(); i)循环中&#xff0c;我们 定义了…

app开发者提升第四季度广告收入的方法

第四季度将迎来双十一、双十二、圣诞、元旦为主的电商购物季&#xff0c;这是一年中利用线上消费为全新年度和全新预算做好准备的最佳时机&#xff0c;从过往的变现成功案例中汇总了优化要点&#xff0c;帮助开发者在第四季度和未来一年获取更多广告收益。 https://www.shensh…

利用IT服务台软件提高客户满意度的4种方法

客户满意度在整个业务过程中常常会被忽视&#xff0c;但其却是业务增长的重要因素。显而易见&#xff1a;满意的客户会在曾购买过的产品上停留更长的时间&#xff0c;也更有可能交叉购买或再次购买。他们会表现出品牌忠诚度&#xff0c;通常会增加在贵公司的平均消费金额。 而满…