字符串系列之滑动窗口

大家好,滑动窗口算法一般是作用在字符串或者数组上,通过不断的滑动逻辑窗口,在特定窗口大小内进行计算的过程。滑动窗口的方式可以降低时间复杂度,从而减短计算的执行时间。

比如说在字符串s="5189623196" 中,窗口大小 w=5,找出每个窗口字符子串的和。

普通的计算方式:每 5 个进行一次求个计算,时间复杂度方面能够达到 O(N^2)

利用窗口的方式:可很大程度可以减小时间方面的消耗。就拿引例来说,时间复杂度应该是O(N)的线性复杂度。

其核心思路是:窗口在每一次的滑动前后,中间元素内容没有改变,仅仅改变的是开头和结尾元素,即:下一窗口内元素之和 = 上一窗口元素和 - 离开窗口元素值 + 新加入窗口元素值。

下面分步骤进行讨论,将最终结果写入到 res 中。

步骤一:直接计算窗口内元素和,res = [29]

图片

步骤二:在上一步骤的基础上,减去离开窗口的元素,加上新加入窗口的元素。

图片

步骤三:继续在上一步骤的基础上,减去离开窗口的元素,加上新加入窗口的元素

图片

相同逻辑继续执行....

步骤六:滑动窗口将最后一个元素包含进来

图片

上述每一步骤中,将 sum 值都添加到最终的结果变量 res 中。滑动窗口这样的思维方式就很巧妙的进行在窗口的不断移动,进而产生各元素在窗口边界进进出出,利用这一点,在时间复杂度方面进行了一个很大提升。

Python代码如下:

class Solution(object):def sumSlidingWindow(self, s, w):sum = 0res = []# 计算第一个窗口数字之和for item in range(5):sum += s[item]res.append(sum)# 后面利用滑动窗口思想进行计算for item in range(w, len(s)):sum -= s[item-w]sum += s[item]res.append(sum)return res

下面用两个 LeetCode 经典案例进一步阐述滑动窗口这类题目。

1.字符串的排列

给两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。

换句话说,s1 的排列之一是 s2 的 子串 。

输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

首先,两个字符串的排列,首先可以想到字典,根据字典去比较。词典可根据两个不同字符串将给到的字符进行计数,因此,这样很同容易进行比较。

另外,在遍历 s2 的时候,使用滑动窗口,该窗口的长度是字符串 s1 的长度,遍历 s2 使得字符不断的出入,在此过程中比较两个字典。

整个过程,记 s1 的长度为 size1s2 的长度为 size2,窗口大小为 size1,不断遍历 s2,直到 s2 的结尾。

记  s1 的字典为 dict_s1,记s1的字典为dict_s2,在遍历s2的过程中,每滑动一次窗口,比较 dict_s1 和 dict_s2 是否相等。

窗口滑动过程中,需要删除滑出窗口的元素以及新增滑入窗口的元素。

删除滑出窗口的元素:广义上是从字典 dict_s2 中删除,注意此处不能直接删除,因为可能会有重复元素存在,比如:{'a':2},所以只能是将 value 值减 1。之后再判断 value 值为否为 0,如果为 0 了,这个时候就可以直接将该元素从字典中删除。

新增滑入窗口的元素:直接将滑入窗口的元素添加进字典中。

下面用几个图清晰的一步一步操作:

初始化 s1 对应的字典 dict_s1={'a':1,'b':1}

① s2 对应的字典为图中窗口中的元素 dict_s2={'e':1,'i':1}

最后判断 dict_s1 != dict_s2

图片

② 向右滑动窗口,由于元素e的 value 值为 1,直接删除该元素,加入新元素d,此时,dict_s2={'i':1,'d':1}

最后判断 dict_s1 != dict_s2

图片

③ 继续向右滑动窗口,由于元素 i 的 value 值为 1,直接删除该元素,加入新元素 b,此时,dict_s2={'d':1,'b':1}

最后判断 dict_s1 != dict_s2

图片

③ 继续向右滑动窗口,由于元素 d 的 value 值为 1,直接删除该元素,加入新元素 a,此时,dict_s2={'b':1,'a':1}

最后判断 dict_s1 == dict_s2,此时可以直接返回 True。不用在继续窗口滑动了。

图片

这个题虽然在 LeetCode 中标记为中等,但是用滑动窗口的方式去解决还是非常容易理解的。

下面是 Python 代码的实现:

class Solution(object):def checkInclusion(self, s1, s2):size1 = len(s1)size2 = len(s2)if size1 > size2:return Falsedict_s1 = collections.Counter(s1)dict_s2 = collections.Counter(s2[:size1-1])left = 0for right in range(size1-1, size2):# 增加新添加的元素dict_s2[s2[right]] += 1# 判断如果字典相同,则返回 Trueif dict_s1 == dict_s2:return True# 删除左边剔除的元素(首先需要将左边元素的 value 值减 1)dict_s2[s2[left]] -= 1# value 值为 0 的时候,就可以删除该元素了if dict_s2[s2[left]] == 0:del(dict_s2[s2[left]])# 窗口整体向右移动一格left += 1right += 1return False

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

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

想要判断字符是否存在,通常会使用到字典结构来判断是否存在。

首先,初始化窗口左右端 left=0 和 right=0,初始化字典 words 用来存放已加入字符的元素(key)和元素下标(value),例如,words={'a':0,'b':1,'c':2}

然后,right 指针向右移动,判断新加入元素是否存在于字典中,如果存在,让窗口左边界指向字典对应 value 值的下一个位置,并且更新字典中对应元素的下标值(value);如果不存在,则加入字典 words中;

注意点:

中间有可能 left 已经更新到后边,而新判断的元素可能在前面,会导致 left 变小,所以需要采用 max 来进行判断

举例:abba,执行到第二个 b 的时候,left=2,此时words={'a':0,'b':2}。后面再判断最后的 a 的时候,就会使得 left=0+1=1

因此,需要 left = max(words.get(v) + 1, left)

每一步骤记录最长子串长度,依旧用几个图清晰的一步一步操作:

① left=0, right=0, 加入新元素 'a',words={'a':0}, 窗口长度为1。

图片

② left=0, right=1, 加入新元素 'b',words={'a':0, 'b':1}, 窗口长度为2。

图片

③ left=0, right=2, 加入新元素 'c',words={'a':0, 'b':1, 'c':2}, 窗口长度为3。

图片

④ left=0, right=3, 加入新元素 'a';

发现 a 已经在 words={'a':0, 'b':1, 'c':2} 存在;

则更新 left=max(words.get('a') + 1, left)=1,words={'a':3, 'b':1, 'c':2},窗口长度为3。

图片

⑤ left=1, right=4, 加入新元素 'b';

发现 'b' 已经在 words={'a':3, 'b':1, 'c':2} 存在;

则更新 left=max(words.get('b') + 1, left)=2,words={'a':3, 'b':4, 'c':2},窗口长度为3。

图片

⑥ left=2, right=5, 加入新元素 'c';

发现 'c' 已经在 words={'a':3, 'b':4, 'c':2} 存在;

则更新 left=max(words.get('c') + 1, left)=3,words={'a':3, 'b':4, 'c':5},窗口长度为3。

图片

⑦ left=3, right=6, 加入新元素 'b';

发现 b 已经在 words={'a':3, 'b':4, 'c':5} 存在;

则更新 left=max(words.get('b') + 1, left)=5,words={'a':3, 'b':6, 'c':5},窗口长度为2。

图片

⑧ left=5, right=7, 加入新元素 'b';

发现 'b' 已经在 words={'a':3, 'b':6, 'c':5} 存在;

则更新 left=max(words.get('b') + 1, left)=7,words={'a':3, 'b':7, 'c':5},窗口长度为1。

根据每一步骤记录的窗口长度,可得到最长窗口为 3。下面就由上述逻辑得到最终的代码:

class Solution(object):def lengthOfLongestSubstring(self, s):if not s:return 0left, right = 0, 0length = 1words = {}for right, v in enumerate(s):if s[right] in words.keys():print(words.get(v) + 1, left)left = max(words.get(v) + 1, left)length = max(right-left+1, length)# 将当前值的 value 值覆盖words[v] = rightprint(words)return length

综上所述,本文字符串系列有关滑动窗口的介绍已经完成,关于其他滑动窗口问题以后会继续总结。

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

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

相关文章

【51单片机Keil+Proteus8.9】AT89C51+7段数码管两位计数器

两位计数器显示 设计思路 电路设计 使用AT89C51单片机,外部引出两个管脚P3^3和P3^4当作按钮分别作为start和stop,对计时状态进行控制,由于是两位计数器,引出两个管脚P3^0和P3^1作为两个数码管的片选信号,工作时快速…

每日温度00

题目链接 每日温度 题目描述 注意点 列表对应位置的输出为:要想观测到更高的气温如果气温在这之后都不会升高,请在该位置用 0 来代替 解答思路 利用栈先进后出的特点将低温度的下标存储到栈中,如果当前温度比栈顶下标对应温度更高&#…

2024--Django平台开发-订单项目管理用户认证+动态菜单+权限控制(十三)

day13 订单管理项目开发 1.表结构设计 1.1 abstract类 from django.db import modelsclass ActiveBaseModel(models.Model):active models.SmallIntegerField(verbose_name"状态", default1, choices((1, "激活"), (0, "删除"),))class Meta:…

【Java】HttpServlet类中前后端交互三种方式(query string、form表单、JSON字符串)

在前后端的交互中,前端通过以下三种方式来与后端进行交互🌟 ✅query string ✅form表单 ✅JSON字符串 下面我们将书写这三种方式的后端代码并进行讲解 1、Query String QueryString即在url中写入键值对,一般用doGet方法进行交互 代码如下 …

『MySQL快速上手』-⑩-索引特性

文章目录 1.索引的作用2.索引的理解建立测试表插入多条记录查看结果 2.1 MySQL与磁盘交互的基本单位2.1 为何IO交互要是 Page2.3 理解单个Page2.4 理解多个Page2.5 页目录2.6 单页情况2.7 多页情况2.8 B vs B2.9 聚簇索引 vs 非聚簇索引非聚簇索引聚簇索引 3.索引操作3.1 创建主…

Redis实战之-分布式锁

一、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行…

『Open3D』1.10 Tensor数据处理

open3d中实现了自身的数据类型,用于open3d中内部算法的数值计算,但基础使用上与numpy类似。 目录 1、tensor创建 2、tensor数据属性 3、 Tensor数据在CPU与GPU上的转换

51单片机中断系统编程

一.外部中断 1.编程思想 中断准备:中断初始化函数打开中断开关 ,选择中断传输方式中断处理:为了便于观察,让我们知道单片机进入中断处理函数,在这里我们选择打开流水灯电路搭建:由于P3^3引脚不便直接接地…

用LED数码显示器循环显示数字0~9

#include<reg51.h> // 包含51单片机寄存器定义的头文件 /************************************************** 函数功能&#xff1a;延时函数&#xff0c;延时一段时间 ***************************************************/ void delay(void) { unsigned …

C语言总结十二:文件操作详细总结

在操作系统中&#xff0c;为了统一对各种硬件的操作&#xff0c;简化接口&#xff0c;不同的硬件设备也都被看成一个文件。对这些文件的操作&#xff0c;等同于对磁盘上普通文件的操作。我们不去探讨硬件设备是如何被映射成文件的&#xff0c;把任意 I/O 设备&#xff0c;转换成…

Doris配置外表以及多个Hive外表的配置

1.场景分析 以Clickhouse、Doris、Starrocks等为代表的mpp分析数据库正在快速的兴起&#xff0c;以其高效查询、跨库整合能力收到广大技术人员的喜爱。本文主要浅显介绍下作者在使用Doris时&#xff0c;通过建立catlog进行跨库查询。 废话不多少&#xff0c;直接上代码 2.相关…

使用Python+pygame实现贪吃蛇小游戏

使用Pythonpygame贪吃蛇小游戏 使用第三方库pygame&#xff0c;关于Python中pygame游戏模块的安装使用可见 https://blog.csdn.net/cnds123/article/details/119514520 给出两种实现。 第一种 运行效果如下&#xff1a; 游戏源码如下&#xff1a; import pygame import sy…