862. 和至少为 K 的最短子数组

题目描述

数组含有负数,求一个最短子数组,子数组的和>=k

f1-单调队列

基本分析

  1. 子数组和的问题怎么优化?前缀和
  2. 相比于暴力去枚举,可以有哪两个思路优化?用一个数据结构来存可能的前缀,(1)过河拆桥,如果一个前缀值满足了条件,那么他不用再考虑了,从前面弹出;(2)后浪更迭,当前遍历到的值<= 尾部的值,尾部的值需要弹出。最终保证q中的索引对应的s值是单调上升的。

代码

显式计算前缀和

class Solution:
    def shortestSubarray(self, nums: List[int], k: int) -> int:
        ans = inf
        s = list(accumulate(nums, initial = 0))
        q = deque()

        for i, cur_s in enumerate(s):
            # 过河拆桥
            while q and cur_s - s[q[0]] >= k:
                ans = min(ans, i - q[0])
                q.popleft()
            # 后浪推前浪
            while q and s[q[-1]] >= cur_s:
                q.pop()

            q.append(i)
        
        return ans if ans < inf else -1
一边遍历一边计算

class Solution:
    def shortestSubarray(self, nums: List[int], k: int) -> int:
        ans = inf
        q = deque([(0, 0)])
        cur_s = 0

        for i, num in enumerate(nums, 1):
            cur_s += num
            while q and cur_s - q[0][0] >= k:
                ans = min(ans, i - q[0][1])
                q.popleft()
            while q and cur_s <= q[-1][0]:
                q.pop()

            q.append((cur_s, i))
        
        return ans if ans < inf else -1

总结

  1. 去掉前缀和的写法需要注意什么?(1)nums遍历如果从0开始,队列中需要放下标-1;(2)因为没有显式写出前缀和数组,求之前的时候就需要从队列中去拿,所以队列存的时候除了索引,还要存对应的前缀和的值。
  2. 计算长度的时候为啥不+1?cur_s对应的区间是0-i-1的和,q[0]对应的区间是0-q[0]-1的和,子区间的索引是q[0]到i-1,所以不再额外+1。
posted @ 2023-03-16 15:52  zhangk1988  阅读(39)  评论(0)    收藏  举报