滑动窗口思想:从左至右滑动,不满足时,右边沿前进;窗口内子串满足时,左边沿前进缩小子串范围。
下面是一些你可以用来确定给定问题可能需要滑动窗口的方法:
问题的输入是一种线性数据结构,比如链表、数组或字符串
你被要求查找最长/最短的子字符串、子数组或所需的值
你可以使用滑动窗口模式处理的常见问题:
大小为 K 的子数组的最大和(简单)
带有 K 个不同字符的最长子字符串(中等)
寻找字符相同但排序不一样的字符串(困难)
一、所有的子数组中累加和为k的最长子数组长度:
给定一个无序数组arr,其中元素可正,可负,可0,给定一个整数k。
求arr 所有的子数组中累加和为k的最长子数组长度。
输入 [1,-2,1,1,1]
2
输出 5
# 要找的目标子串符合条件:s2-s1=k,用dict_s1记录{s1的和:s1的下标}
# s1的下标从-1到p ,s2的下标从0到q
class Solution:
def FindMaxLenOfSubStr(self, arr: list[int], k: int) -> int:
dict_s1 = {0: -1}
Mlen, sum_s2 = 0, 0
p, q = 0, 0
for i, num in enumerate(arr):
sum_s2 += num
if sum_s2-k in dict_s1:
if Mlen < i - dict_s1[sum_s2-k]:
Mlen = i - dict_s1[sum_s2-k]
p = dict_s1[sum_s2-k]
q = i
# 记录目标子串的起始前和结束下标
if sum_s2 - k not in dict_s1:
dict_s1[sum_s2 - k] = i
print(arr[p+1: q+1])
print(p, q)
return Mlen
if __name__ == '__main__':
x = Solution()
print(x.FindMaxLenOfSubStr([1, -2, 1, 1, 1], 2))
相似例题:给定一个数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。
子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组
一样的解法,将字符串换成数组而已:
class Solution:
def maxLength(self , arr ):
# write code here
if not arr:
return 0
Mlen = 0
win = []
for i,n in enumerate(arr):
if n in win:
win = win[win.index(n)+1:]
win.append(n)
Mlen = max(Mlen, len(win))
return Mlen
二、求数组中满足和>=s 的长度最小连续子数组
给定一个含有n个正整数数组和正整数s,找出该数组中满足和大于等于s的长度最小连续子数组。不存在返回0
示例: s=7,nums= [2,3,1,2,4,3]
输出2
class Solution:
def MinlenOfSubstr(self, nums, s):
Mlen, sum = len(nums), 0
window = []
flag = 0 #记录滑动过程中是否满足
for i,n in enumerate(nums):
sum += n
#不满足时,窗口右沿扩展
if sum < s:
window.append(n)
else:
window.append(n)
#满足时,窗口左沿缩小
while sum >= s:
flag = 1
if Mlen > len(window):
Mlen = len(window)
sum -= window[0]
window.pop(0)
if flag == 0:
return 0
else:
return Mlen
三、求字符串内无重复子串的最长长度 / 最长无重复子数组
思想:求两个相同字符相隔的最长长度
解法一:定义一个列表作为滑动窗口,不符合时扩展右沿;符合时将左沿缩至重复的下标+1。
目的:求出重复字符相隔的最大长度。
class Solution:
def MLenOfSubStr(self, s: str) -> int:
if not s:
return 0
list_win = [] #定义滑动窗口
Mlen = 0
for i, a in enumerate(s):
if a not in list_win:
list_win.append(a) #不重复的话右沿扩展
else:
#!!重复的时候,取重复字符下标+1位开始,重新计算。
list_win = list_win[list_win.index(a)+1:]
list_win.append(a) #重复也得后走
Mlen = max(Mlen, len(list_win))
return Mlen
if __name__ == "__main__":
x = Solution()
print(x.MLenOfSubStr("abccdbaab"))
四、求数组中和为目标值的两个数
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
if not nums:
return []
dst = [] #定义列表而不是字典,可以根据值找到索引
for i, n in enumerate(nums):
dst = nums[:i] #有序确保了索引能找到
if target-n in dst:
ix = dst.index(target-n)
return [ix, i]
return [ ]
五、求arr中所有子数组中累计和为k的最长子数组长度。
输入: [1, -2, 1, 1, 1]
输出: 5
class Solution:
def FindMaxLenth(self, arr):
Mlen, sum = 0, 0
dict_s1 = {0: -1} # key为 s1的和,value为s1的结束位置。
#假设和为k的目标串 为s2-s1
for i,n in enumerate(arr):
sum += n #s2的和,k=s2-s1
if sum-k in dict_s1:
Mlen = max(Mlen, i-dict_s1[sum-k])
else:
dict_s1[sum-k] = i #-1到i时
return Mlen