滑动窗口用法

文章目录

  • 1. 长度最小的子数组(模板)
  • 2. 无重复字符的最长字串
  • 3. 最小覆盖字串
  • 4. 加油站
  • 5. 替换字串得到平衡字符串

1. 长度最小的子数组(模板)

image-20240310211503255

  1. 题目分析
直接用步骤分析示例1,[]表示窗口,min_length表示满足条件的最短子数组,sum表示窗口内元素的总值:
	- [2],3,1,2,4,3		min_length=100001,sum=2 < target=7
	- [2,3],1,2,4,3		min_length=100001,sum=5 < target=7
	- [2,3,1],2,4,3		min_length=100001,sum=6 < target=7
	- [2,3,1,2],4,3		min_length=4,     sum=8 >= target=7
	- PS:只要sum>=target,就尝试将左窗口向右移动,若仍满足条件,就更新min_length,不行就继续
	- [2,3,1,2,4],3		min_length=5,	  sum=12 >= target=7
	- 2,[3,1,2,4],3		min_length=4,	  sum=10 >= target=7
	- 2,3,[1,2,4],3		min_length=3,	  sum=7 >= target=7
	- 2,3,[1,2,4,3]		min_length=4,	  sum=10 >= target=7
	- 2,3,1,[2,4,3]		min_length=3,	  sum=9 >= target=7
	- 2,3,1,2,[4,3]		min_length=2,	  sum=7 >= target=7
	- 最后返回min_length=2
  1. 代码实现
def minSubArrayLen(target, nums):
    """
    :type target: int
    :type nums: List[int]
    :rtype: int
    """
    n = len(nums)
    
    # l表示左窗口的位置,r表示右窗口的位置
    l = r = 0
    # sum表示窗口内的元素总值
    sum = nums[0]
    
    # 如果第一个元素的值就大于等于target,直接返回
    if sum >= target:   
        return 1
    
    # 设置成100001是因为测试用例的数组长度最长为100000
    min_length = 100001
    
    # 循环n - 1次
    for i in range(1, n):
        # 右窗口肯定是要一直移动的,不存在每次循环不移动的情况
        r += 1
        sum += nums[r]
        
        # 如果sum去掉靠近左窗口的那个元素后仍然大于sum,就该减小窗口的长度了
        while sum - nums[l] >= target:
            sum -= nums[l]
            l += 1
        
        # 经过上述步骤后,当来到右窗口此时的位置时,满足条件的窗口长度就得到了
        if sum >= target:
            min_length = min(min_length, r - l + 1)
    
    # 数组所有元素的总和加起来都小于target,min_length就返回0
    return 0 if min_length == 100001 else min_length

image-20240310215745912

  1. 精简版
def minSubArrayLen(target, nums):
    """
    :type target: int
    :type nums: List[int]
    :rtype: int
    """
    n = len(nums)

    l = r = 0

    min_length = 100001

    sum = 0

    while r < n:
        sum += nums[r]

        while sum - nums[l] >= target:
            sum -= nums[l]
            l += 1

        if sum >= target:
            min_length = min(min_length, r - l + 1)

        r += 1

    return 0 if min_length == 100001 else min_length

image-20240310221133426

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

image-20240310222352348

注意s字符串包含:字母、数字、符号和空格

  1. 题目分析:
以abcabcbb为例,max_length表示最长无重复字串字串,l表示左窗口,r表示右窗口:
	- [a]bcabcbb		max_length=1
    - [ab]cabcbb		max_length=2
    - [abc]abcbb		max_length=3
    - [abca]bcbb
    - 下面这一步就很重要了,l=max(l,a上一次出现位置+1)
    - a[bca]bcbb		max_length=3
    - a[bcab]cbb		l=max(l,b上一次出现位置+1)
    - ab[cab]cbb		max_length=3
    - ab[cabc]bb		l=max(l,c上一次出现位置+1)
    - abc[abc]bb		max_length=3
    - abc[abcb]b		l=max(l,b上一次出现位置+1)
    - abcab[cb]b		max_length=3
    - abcab[cbb]		l=max(l,b上一次出现位置+1)
    - abcabcb[b]		max_length=3
  1. 代码实现
def lengthOfLongestSubstring(s):
    """
    :type s: str
    :rtype: int
    """
    n = len(s)
	
    # 字母、数字、空格和一些符号的ascii码都在0~255之间
    pos = [-1] * 255

    l = 0

    max_length = 0

    for r in range(n):
        # 在r移动到下一个字符位置时,一定要先让l获取上次这个字符出现的位置并且加1
        l = max(l, pos[ord(s[r])] + 1)
        # 然后再更新这个字符出现的位置
        pos[ord(s[r])] = r
        # 最后通过比较获得wu'c
        max_length = max(max_length, r - l + 1)


    return max_length

image-20240310230922966

3. 最小覆盖字串

image-20240311171925285

  1. 题目分析
1. 拿一个cnts数组统计t串中字符的出现次数,total记录t中每个字符出现次数的总和
2. 首先,t中字符每出现一次,cnts[ord(t[i])] -= 1
3. 然后开始循环s字符串
4. 如果遇到出现次数小于0的,total就减-1
5. 每循环一次,该字符的出现次数就加1(我们只关注t中的字符,s中其它字符正常加就行了,后面不会涉及到它们)
6. 当total == 0时,也就是说当前窗口中已经包含了t中所有字符
7. 开始判断,左窗口所指向的字符出现次数是否大于0,大于0左窗口就可以向右移动了
8. PS:当total第一次等于0时,表示窗口中恰好有t中每个字符,且它们的出现次数都为0,后续可能会有多余的,它们的出现次数就会大于0,s中其它字符最开始都是0,只要在窗口中,它们的出现次数就会大于0
9. 然后判断当前窗口的长度跟最小覆盖字串的长度比较,取较小的那个

cnts是统计所有字符的出现次数,初始化为0
	- 首先是将t串中每个字符的出现次数减1,也就是说,除了t串中的字符出现次数是负数,其它字符的出现次数都是0
	- 然后就是只要有一个字符进窗口,出现次数加1
	- 当左窗口字符出现次数大于0时,就可以考虑将其删除了
  1. 代码实现
def minWindow(s, t):
    """
    :type s: str
    :type t: str
    :rtype: str
    """
    n = len(s)
    m = len(t)
    
    # 如果s串长度小于m长度,s就不可能有长度大于等于m的字串
    if n < m:
        return ""

    total = m
    
    # 计数数组
    cnts = [0] * 255
    # 给t串中每种字符出现次数为负数
    for i in range(m):
        cnts[ord(t[i])] -= 1
    
    l = 0
    target = ''
    min_length = 100001
    
    # 开始遍历s串
    for r in range(n):
        
        # 能满足下面if语句条件的一定是t串中的字符
        if cnts[ord(s[r])] < 0:
            total -= 1
        
        # 不管是哪个串中的字符,次数都要加1
        cnts[ord(s[r])] += 1

        # total==0表示窗口中已经包含了t串所有字符
        if total == 0:
            
            # 开始判断左窗口可不可以移动
            while cnts[ord(s[l])] > 0:
                cnts[ord(s[l])] -= 1
                l += 1
                
            # 取最小覆盖字串的长度
            if r - l + 1 < min_length:
                min_length = r - l + 1
                target = s[l : r + 1]

    return target

image-20240311201530260

4. 加油站

image-20240311210500668

  1. 题目分析
用左窗口遍历一遍加油站即可,此时左窗口就相当于起点,右窗口就从左窗口开始向后遍历,只要出现油不够的情况,左窗口就向右移动。

可以将gas和cost数组合并来看,例如:
gas 	= [1,2,3,4,5]
cost	= [3,4,5,1,2]
combine = [-2,-2,-2,3,3]
combine[0]表示从第0个加油站开到第1个加油站倒欠2升油,所以无法以第0个加油站为起点,开到第一个加油站
combine[3]表示从第4个加油站开到第5个加油站多出了3升油,所以能以第4个加油站为起点,开到第五个加油站
  1. 代码
def canCompleteCircuit(gas, cost):
    """
    :type gas: List[int]
    :type cost: List[int]
    :rtype: int
    """
    n = len(gas)

    # sum表示路上的总油量,只要油量小于0了,就不能走
    sum = 0

    # len表示一共走了几个加油站
    length = 0

    for l in range(n):


        while sum >= 0:
            # 如果从某个加油站出发,能走完一圈,就返回那个加油站的位置
            if length == n:
                return l
            r = (l + length) % n
            length += 1
            sum += gas[r] - cost[r]

        # 走到这说明sum<0了,那么左窗口就该移动了,且sum要减去左窗口移动之前所在位置的那个值,len也要减1
        sum -= gas[l] - cost[l]
        length -= 1

    # 如果以每个加油站为起点出发,sum最后都会小于0,就返回-1
    return -1

image-20240311212755937

5. 替换字串得到平衡字符串

image-20240311221316817

  1. 题目分析
思路:
1. 先统计每种字符的出现次数
2. 然后用窗口框柱一段字符,假设窗口外的每种字符出现次数不等于 n/4,判断只替换窗口内的字符能否使窗口外的每种字符字符出现次数都变成n/4
3. 如果该窗口不行,那么它的子窗口一定不行(这里没想出为什么)如果[l,r-1]不行,[l,r]行,那么[l+1,r]也有可能行,[l+1,r-1]就一定不行

4. 那么怎么实现第二步?
   假如窗口长度为8,窗口外面字符出现次数为Q->18,W->19,E->16,R->19,要求是每种字符出现20次
   8-(20-18) = 6
   6-(20-19) = 5
   5-(20-16) = 1
   1-(20-19) = 0
   最后剩余0,表示能行
   
   如果外面某种字符超过了平均出现次数,那么这个窗口一定不行
  1. 代码实现
def balancedString(s):
    """
    :type s: str
    :rtype: int
    """
	# 这个函数就是实现上面分析的第四步
    # len表示窗口的大小,也就是有len个元素可以替换,看能不能使窗口外面的每种字符出现次数达到aver
    def ok(cnts, len, aver):
        for i in range(4):
            if cnts[i] > aver:
                return False
            len -= aver - cnts[i]

        return True if len == 0 else False
    
    n = len(s)

    aver = n // 4

    # 将Q映射成0,W映射成1,E映射成2,R映射成3
    data = ['Q', 'W', 'E', 'R']
    cnts = [0] * 4
    for i in range(n):
        cnts[data.index(s[i])] += 1

    # 作为替换字串的最小长度返回
    ans = n

    r = 0
    # 左窗口开始遍历s
    for l in range(n):
        # l-r表示[l,r)范围
        # 如果当前窗口不行,右窗口ji
        while ok(cnts, r - l, aver) == False and r < n:
            # 右窗口每次向右移动都会在窗口内新加入一个字符,它在窗口外的出现次数要减1
            cnts[data.index(s[r])] -= 1
            r += 1

        # 这里是为了判断上面循环是因为第一个条件结束还是因为第二个条件结束的
        # 若是因为第二个条件结束的,就说明这个窗口是合法的,当然这里只能判断第一个条件
        # 因为第一个条件也有可能不符合
        if r != n:
            ans = min(ans, r - l)

        # 最后一次循环结束后,左窗口会向右移动,相当于会将它原来所在位置的字符排除到窗口外,所以在窗口外这个字符的出现次数要加1
        cnts[data.index(s[l])] += 1
    return ans

image-20240311230406213

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

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

相关文章

探索网络爬虫:技术演进与学习之路

网络爬虫及IP代理池 前言爬虫技术的演进最新的爬虫技术爬虫技术学习路线 前言 在信息时代&#xff0c;网络爬虫技术作为获取和处理网络数据的重要手段&#xff0c;已经成为数据科学、机器学习和许多商业应用的基石。从简单的HTML页面抓取到复杂的动态内容采集&#xff0c;爬虫…

Excel---一个工作簿中的多个sheet合并成一个PDF

0 Preface/Foreword 1 操作方法 1.1 方法一 文件》 导出 》创建PDF/XPS 》 选项 》发布内容 》“整个工作簿” 1.2 方法二 文件》 打印》 打印机选项中&#xff0c;选择一种PDF阅读器 》设置选项中&#xff0c;选择打印整个工作簿。

Linux:软硬链接及动静态库

一、Linux中的链接文件 1.1硬链接及应用场景 ln//创建硬链接 我们再创建一个硬链接生成的文件。 我们可以看到mlink.hard的inode和makefile.c的inode都是一样的&#xff0c;inode一样里面的数据自然也是一样。相当于对make.file进行了一个重命名&#xff0c;所以硬链接一定没…

计算机网络—HTTPS协议详解:工作原理、安全性及应用实践

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;ヒューマノイド—ずっと真夜中でいいのに。 1:03━━━━━━️&#x1f49f;──────── 5:06 &#x1f504; ◀️ ⏸…

雨云:不只是一阵清风,更是一场暴雨的力量

引言 在网络时代&#xff0c;服务器是任何在线业务的核心。无论你是运营一家小型博客还是承载着数百万用户的大型电商平台&#xff0c;都需要一个稳定、高效的服务器来支持你的业务。然而&#xff0c;在众多服务器提供商中&#xff0c;有一家备受推崇&#xff0c;那就是雨云。 …

electron打包编译国产统信uos系统 arm架构 x86架构 linux mac等环境

electron v21版本以上统信UOS会提示gbm_bo_map错误&#xff0c;可使用v8~v21版本的electron 打包linux包需要再linux系统下运行编译&#xff0c;arch可以指定架构 如果要在统信uos上运行&#xff0c;需要打包成deb格式&#xff0c;在target中修改成deb 或者用第三方软件把app…

数据库设计说明书(Word模板)

2 数据库环境说明 3 数据库的命名规则 4 逻辑设计 5 物理设计 5.1 表汇总 5.2 表结构设计 6 数据规划 6.1 表空间设计 6.2 数据文件设计 6.3 表、索引分区设计 6.4 优化方法 7 安全性设计 7.1 防止用户直接操作数据库 7.2 用户帐号加密处理 7.3 角色与权限控制 8 数据库管理与维…

深入理解Linux系统中的前后台任务与守护进程

⭐小白苦学IT的博客主页 ⭐初学者必看&#xff1a;Linux操作系统入门 ⭐代码仓库&#xff1a;Linux代码仓库 ❤关注我一起讨论和学习Linux系统 1.前言 在Linux系统中&#xff0c;进程管理是至关重要的一个环节。其中&#xff0c;前后台任务和守护进程是进程管理中不可忽视的两…

Intrigue Core:一款功能强大的攻击面枚举引擎

关于Intrigue Core Intrigue Core是一款功能强大的开源攻击面枚举引擎&#xff0c;该工具可以帮助广大研究人员更好地管理应用程序的攻击面。 Intrigue Core集成了各种各样的安全数据源&#xff0c;可以将这些数据提取到标准化的对象模型中&#xff0c;并通过图形数据库跟踪关…

防错设计及原理

目录 1、防错的作用 2、防错的原理 2.1断根原理 2.2保险原理 2.3自动原理 2.4相符原理 2.5顺序原理 2.6隔离原理 2.7层别原理 2.8复制原理 2.9警告原理 2.10缓和原理 防错法&#xff08;Poka-Yoke&#xff09;&#xff0c;又称愚巧法、防呆法&#xff0c;是一种在作…

二叉查找树、二叉搜索树、二叉排序树算法分析及实现

一、几个概念 二叉树&#xff08;Binary Tree&#xff09;&#xff0c;是 n&#xff08;n > 0&#xff09;个结点&#xff08;每个结点最多只有2棵子树&#xff09;的有限集合&#xff0c;该集合或为空集&#xff0c;称为空二叉树&#xff0c;或由一个根节点和两颗互不相交…

如何编译OpenHarmony自带APP

作者&#xff1a;王石 概述 OpenHarmony 的主干代码是开源社区的重要学习资源&#xff0c;对于想进行应用开发和熟悉 OpenHarmony 能力的同学主干代码是非常重要的资源&#xff0c;在主干代码的 applications 目录里聚集了很多原生的应用实现&#xff0c;那么如何编译这些代码…

java:JUnit单元测试

Junit单元测试 介绍 一个用于编写和执行java单元测试的框架,可以帮助开发人员验证代码 单元测试 一种测试方法,用于校验程序中的最小可测试单元(通常是一个方法)是否按照预期工作. JUnit提供了一组注解和断言方法,使编写和执行单元测试变得更加方便 在开发过程中可以频繁…

HarmonyOS开发实例:【菜单app】

简介 分布式菜单demo 模拟的是多人聚餐点菜的场景&#xff0c;不需要扫码关注公众号等一系列操作&#xff0c;通过分布式数据库可以方便每个人可及时查看到订单详情&#xff0c;数量&#xff0c;总额等&#xff1b;效果如下 demo效果 工程目录 完整的项目结构目录如下 ├…

代码随想录第38天| 509. 斐波那契数 70. 爬楼梯

理论基础 刷题大纲&#xff1a; 动态规划5步曲&#xff1a; 1、确定dp数组以及下标的含义 2、确定递推公式 3、dp数组如何初始化 4、确定遍历顺序 5、举例推导dp数组 509. 斐波那契数 509. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.co…

SpringBoot学习笔记二

SpringBoot学习笔记二 1.SpringBoot配置加载顺序1.1 内部配置加载顺序1.2 外部配置加载顺序 2. SpringBoot整合其他框架2.1 SpringBoot整合Test2.2 SpringBoot整合Redis 1.SpringBoot配置加载顺序 1.1 内部配置加载顺序 同理可知&#xff0c;父项目中的confg下的配置优先级最…

Bitmap OOM

老机器Bitmap预读仍然OOM&#xff0c;无奈增加一段&#xff0c;终于不崩溃了。 if (Build.VERSION.SDK_INT < 21)size 2; 完整代码&#xff1a; Bitmap bitmap; try {//Log.e(Thread.currentThread().getStackTrace()[2] "", surl);URL url new URL(surl);…

【数据结构与算法】之8道顺序表与链表典型编程题心决!

个人主页&#xff1a;秋风起&#xff0c;再归来~ 数据结构与算法 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 目录 1、顺序表 1.1 合并两个有序数组 1.2 原地移除数组中所有的元素va…

2024/4/5—力扣—下一个排列

代码实现&#xff1a; 思路&#xff1a;两遍扫描 void swap(int *a, int *b) {int t *a;*a *b;*b t; }void reverse(int *nums, int l, int r) {while (l < r) {swap(nums l, nums r);l;r--;} }void nextPermutation(int *nums, int numsSize) {int i numsSize - 2;wh…

手把手从零搭建ChatGPT网站midjourney-AI绘画系统,附详细搭建部署教程文档

一、系统前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持…