当前位置: 首页 > news >正文

[leetcode] - 不定长滑动窗口

不定长滑动窗口总体可以分为三大类:
1.求满足小于k的最长子数组/子串
2.求满足至少k的最短子数组/子串
3.求子数组/子串个数

其中第三类又可以分为三小类:
1>满足小于k的子数组/子串的个数
2>满足至少k的子数组/子串的个数
3>恰好等于k的子数组/子串的个数

一、求满足小于k的最长子数组/子串

枚举右端点,while循环处理不满足条件,直到满足条件退出,退出while循环后更新答案。

for循环枚举右端点while 不满足条件移动左端点ans = max(ans, r-l)
# 3. 无重复字符的最长子串
class Solution:def lengthOfLongestSubstring(self, s: str) -> int:if not s:return 0d=defaultdict(int)l,ans=-1,0for r,c in enumerate(s):d[c]+=1while d[c]>1:l+=1d[s[l]]-=1ans=max(ans,r-l)return ans

对于左端点的处理,可以初始化成0,也可以初始化成-1。如果初始化成0,那更新答案时,就是r-l+1。作者更喜欢用-1,所以更新时就是r-l

# 904. 水果成篮 
class Solution:def totalFruit(self, fruits: List[int]) -> int:d=defaultdict(int)l,ans=-1,0for r,f in enumerate(fruits):d[f]+=1while len(d)>2:l+=1d[fruits[l]]-=1if not d[fruits[l]]:del d[fruits[l]]ans=max(ans,r-l)return ans

此类题目经常会把字典的key的个数(也即,种类数)作为while的判断条件,此时在移动左端点更新字典时,如果value=0,必须将key del掉。


二、求满足至少k的最短子数组/子串

枚举右端点,while循环处理满足条件,直到不满足条件退出,在while循环内更新答案。

for循环枚举右端点while 满足条件ans = min(ans, r-l)移动左端点
# 209. 长度最小的子数组
class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:        s,n=sum(nums),len(nums)if s<target:return 0if s==target:return nl,ans,s=-1,inf,0for r,x in enumerate(nums):s+=xwhile s>=target:ans=min(ans,r-l)l+=1s-=nums[l]return ans      
# 76. 最小覆盖子串
class Solution:def minWindow(self, s: str, t: str) -> str:n,m=len(s),len(t)if n<m:return ''cntt=Counter(t)   cnts=defaultdict(int)   #cnts=Counter() l,ans=-1,''for r,c in enumerate(s):cnts[c]+=1while all(cnts[k]>=v for k,v in cntt.items()):#while cnts>=cntt:ans=s[l+1:r+1] if ans=='' or r-l<len(ans) else ansl+=1cnts[s[l]]-=1return ans

注意,如果想要判断一个字典A是否完全包含另一个字典B(即B中的key,A中都有且A中对应key的value都大于B),可以用cntA>=cntB来判断。


三、求子数组/子串个数

3.1 满足小于k的子数组/子串的个数

枚举右端点,while循环处理不满足条件,直到满足条件退出,退出while循环后更新答案。

for循环枚举右端点 while 不满足条件移动左端点ans += r-l
# 713. 乘积小于 K 的子数组
class Solution:def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:mn,n=min(nums),len(nums)if mn>=k:return 0prod=1   l,ans=-1,0for r,x in enumerate(nums):prod*=xwhile prod>=k:l+=1prod//=nums[l]# 学习这种统计子数组的方法ans+=r-lreturn ans

右端点每移动一步,如果满足条件,当前右端点都会和前面的子数组依次组成r-l-1个新的子数组,另外它本身也是一个符合条件的子数组,因此更新答案ans+=r-l

# 2762. 不间断子数组 
class Solution:def continuousSubarrays(self, nums: List[int]) -> int:cnt=defaultdict(int)l,ans=-1,0for r,x in enumerate(nums):# 空间换时间,# 用哈希表减少对重复元素的遍历# 但是如果列表中有大量不重复的元素,用哈希表还是会超时,这时就需要使用单调队列,见1438题cnt[x]+=1while max(cnt)-min(cnt)>2:l+=1cnt[nums[l]]-=1if cnt[nums[l]]==0:del cnt[nums[l]]ans+=r-lreturn ans

本题思路并不复杂,但是如果用max()和min()函数去遍历之前子数组会超时,这时可以使用字典来减少对重复元素的遍历。

# 1438. 绝对差不超过限制的最长连续子数组
# 单调队列,
# 用单调队列记录列表的第一小,第二小,...,第n小;记录列表的第一大,第二大,...,第n大
class Solution:def longestSubarray(self, nums: List[int], limit: int) -> int:mxq,mnq=deque(),deque()   l,ans=-1,0for r,x in enumerate(nums):while mnq and x<=nums[mnq[-1]]:mnq.pop()mnq.append(r)while mxq and x>=nums[mxq[-1]]:mxq.pop()mxq.append(r)while nums[mxq[0]]-nums[mnq[0]]>limit:l+=1if mnq[0]<=l:mnq.popleft()if mxq[0]<=l:mxq.popleft()ans=max(ans,r-l)return ans

有时使用字典依然会超时,这时就必须使用单调队列来存储按序排列的索引值。

# 1918. 第 K 小的子数组和
class Solution:def kthSmallestSubarraySum(self, nums: List[int], k: int) -> int:def check(sum):s=0ll,ans=-1,0for rr,x in enumerate(nums):s+=xwhile s>sum:ll+=1s-=nums[ll]ans+=rr-llreturn ans>=kl,r,ans=min(nums),sum(nums),0while l<=r:mid=(l+r)//2if check(mid):ans=midr=mid-1else:l=mid+1return ans

这题将二分答案和滑动窗口相结合,非常巧妙,值得反复练习和理解。


3.2 满足至少k的子数组/子串的个数

枚举右端点,while循环处理满足条件,直到不满足条件退出,在while循环内更新答案。

for循环枚举右端点while 满足条件ans += n-r移动左端点
# 1358. 包含所有三种字符的子字符串数目 
class Solution:def numberOfSubstrings(self, s: str) -> int:n=len(s)cnt=defaultdict(int)l,ans=-1,0for r,c in enumerate(s):cnt[c]+=1while len(cnt)==3:ans+=n-rl+=1cnt[s[l]]-=1if cnt[s[l]]==0:del cnt[s[l]]return ans

这里的n-r,可以理解为,当前子串是满足条件的最小集合,那后面跟着[0,n-r]个后缀就更满足条件了。因此对于当前子串,共有n-r个满足条件的个数。

# 3298. 统计重新排列后包含另一个字符串的子字符串数目 II 
# 超时
class Solution1:def validSubstringCount(self, word1: str, word2: str) -> int:if len(word1)<len(word2):return 0cnt1,cnt2=Counter(),Counter(word2)n=len(word1)l,ans=-1,0for r,c in enumerate(word1):cnt1[c]+=1while cnt1>=cnt2:ans+=n-rl+=1cnt1[word1[l]]-=1return ansclass Solution:def validSubstringCount(self, word1: str, word2: str) -> int:      cnt2=Counter(word2)k,n=len(cnt2),len(word1)l,ans=-1,0for r,c in enumerate(word1):# 先减频数,再减个数# 通过频数和个数来模拟子串是否覆盖word2,参考76题cnt2[c]-=1if cnt2[c]==0:k-=1while k==0:ans+=n-rl+=1if cnt2[word1[l]]==0:k+=1cnt2[word1[l]]+=1return ans

本题跟76题类似,但是如果采用76题的思路会超时,这时就必须采用先减频数再减个数的方法来模拟是否覆盖。


3.3 恰好等于k的子数组/子串的个数

恰好等于k可以转化为 满足>=k - 满足>k 的个数,而满足>k等价于满足>=k+1,因此可以进一步转化为满足>=k - 满足>=k+1 的个数。

for循环枚举右端点while 满足>=k移动左端点l1while 满足>=k+1移动左端点l2ans += l1 - l2
# 930. 和相同的二元子数组 
class Solution:def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:# 如果数组中有负数,就不能用滑动窗口,滑动窗口必须是单调的,见题560l1=l2=-1s1=s2=0ans=0for r,x in enumerate(nums):s1+=xwhile l1<r and s1>=goal:l1+=1s1-=nums[l1]s2+=xwhile l2<r and s2>=goal+1:l2+=1s2-=nums[l2]ans+=l1-l2return ans
# 560. 和为 K 的子数组
class Solution:def subarraySum(self, nums: List[int], k: int) -> int:# 为什么前缀和需要第一个0, 因为只有在最前面插入一个0,才能用s[2]-s[0]表示以第一个元素开始的子数组[x0,x1]。如果没有插入第一个0,s[0]表示第一个元素,那么s[2]-s[0]就会把第一个元素排除掉。所以必须要在前面插入一个0s=list(accumulate(nums,initial=0)) cnt=defaultdict(int)  ans=0 for x in s:ans+=cnt[x-k]cnt[x]+=1return ans

该题数组中有负数,因此不能使用滑动窗口。



整理自leetcode 灵神题单:
https://leetcode.cn/discuss/post/3578981/ti-dan-hua-dong-chuang-kou-ding-chang-bu-rzz7/

http://www.dtcms.com/a/349152.html

相关文章:

  • 深度学习卷积神经网络项目实战
  • 电容触控:自电容 vs 互电容
  • Rust 登堂 生命周期(一)
  • 内网后渗透攻击--域控制器安全(1)
  • 控制启动过程
  • 【typenum】 25 去除无符号整数前导零的特性(private.rs片段)
  • 重塑招聘战场:AI得贤招聘官AI面试智能体6.3如何用“精准”重新定义人才筛选?
  • C++(String):
  • 2025 年 8 月 22 日科技前沿:技术突破与范式跃迁的交汇点
  • golang1 专栏导学
  • 算法题(190):食物链(带权并查集)
  • leetcode 162 寻找峰值
  • 1、vue2面试题--生命周期
  • Goang开源库之go-circuitbreaker
  • HTTP请求中的CGI请求与登录注册机制
  • AI大模型企业落地指南-笔记01
  • Data_Formats_GRIDGeoTIFFShapeFile
  • 数据产品(2)用户画像数据分析模型
  • 【计算机视觉】CaFormer
  • 房屋装修设计管理系统的设计与实现/房屋装修管理系统
  • 审核问题——应用未配置图标的前景图和后景图
  • 深度剖析Spring AI源码(十):实战案例:智能客服系统
  • MySQL-delete tableName from ...
  • [GeographicLib] LocalCartesian用法
  • 从成本中心到价值创造者:网络安全运维的实施框架与价值流转
  • 深入两种高级并发设计模式
  • 零基础玩转STM32:深入理解ARM Cortex-M内核与寄存器编程
  • 奈飞工厂:算法优化实战
  • MySQL性能优化的5个维度
  • 02-开发环境搭建与工具链