求某个数组中符合某个条件的最长/短子数组问题
最典型的问题:M209长度最小的子数组
子数组: 某个数组中连续的一段
这种连续子数组的问题与以前那种常常想到的动态规划不同,这里不能使用dp,因为整个大问题不能拆分为为更小的问题,整个数组中的数据都是没有关系的
但是,给定的条件一般都满足某种情况使得我们可以使用双指针求解,最典型的就是上面M209这道题目,数组中的所有数都是整数,这意味着如果当前子数组中的数之和==target之后再添加到娥话子数组之和必然增加,不要看不起这个不起眼的条件,这个条件是使我可以在子数组之和 >= target之后从左边缩小子数组的大小
这种题目一般的思路是:
双指针,两个指针同向出发,right走在前面,left走在后面,两个指针范围之内的子数组就是符合要判断的子数组,以M209为例,先left不动,right一直往右扩张,不断计算 t = sum[left:right+1],一旦t>=target就定住right移动left直到 t <=target,之所以可以这样做是因为这一题是寻找最短的子数组,如果 [left, right]刚好满足sum>=tatget,又由于right是刚进来的,若移动left之后[left, right]仍然满足条件那么 left之前的所有位置到right都满足条件!
M209题解如下:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
ans = float('inf')
left = 0
sum_ = 0
for right in range(len(nums)):
sum_ += nums[right]
while sum_ - nums[left]>=target:
sum_ -= nums[left]
left+=1
if sum_>=target and (t:=right-left+1)<ans:
ans = t
return ans if ans!=float('inf') else 0
另外的情况下,隐含的条件比较难想但是对于这个连续子数组的问题往双指针滑动窗口上面想总是没错的
比较难找条件的一道题:M6293统计好子数组的数目
def countGood(self, nums: List[int], k: int) -> int:
# 这题有个小点,如果不知道其实还挺难想的,即使了解这个双指针滑动窗口
# 如果cnt[2]=8,那么如果再包裹进来一个2那么pairs会直接+8,因为新进来的2会和里面
# 原来的8个2每一个配对一次,所以是+8
cnt = Counter()
left = 0
n = len(nums)
pairs = 0
ans = 0
for right in range(n):
# 这里的pairs 就相当于M209的sum(子数组),某种意义上的单调递增
pairs+=cnt[nums[right]]
cnt[nums[right]]+=1
while pairs - (cnt[nums[left]]-1)>=k:
pairs -= cnt[nums[left]]-1
cnt[nums[left]]-=1
left+=1
if pairs>=k:
ans+=left+1
return ans