LeetCode 862. Shortest Subarray with Sum at Least K

28 阅读1分钟

🔗leetcode.com/problems/sh…

题目

  • 给定一个数组,若有子数组之和 ≥ k,返回子数组长度的最小值
  • 若不存在这样的子数组,返回 -1

思路

  • l ≤ r, left ≤ right

  • 子数组 [l, r] 的和,为 presum[r] - presum[l-1]

  • 满足条件的子数组为 presum[r] - presum[l] ≥ k,更新满足条件的子数组长度的最小值,r - l + 1

  • 按照顺序遍历 presum[r],核心是找到满足 presum[l] ≤ presum[r] - k,并且 l 要最大;或者反过来,按照顺序遍历 presum[l],找到满足条件的最小 right index

  • 单调递增队列可以完美解决该问题,单调队列记录递增的 presum 值对应的最大 index;

  • 假设当前遍历至 index,

    • 若 queue 的头 presum[queue.front()] ≤ presum[index] - k, 那么更新满足条件的长度;后续的 index 若有满足条件的,那么必定比当前的数组长度长,queue.pop_front 可以不再参与计算;
    • 若 queue 的尾 presum[index] ≤ presum[queue.back()], 因为要最小长度的子数组,只需要记录当前 index 和 presum[index] 即可;若后续的 presum[index + x] - presum[queue.back()] ≥ k,presum[index] 比 presum[queue.back()] 小,更可以满足该条件,子数组长度也更小,queue.back 可以不再参与计算;
    • 反之,直接记录当前 presum 的 index,加入到队列尾部;

要点

  • monotonic-queue 单调队列的应用场景为单调栈和滑动窗口的最值 ,时间复杂度为 o(n)
  • 题目 hard 的点在于,可以把问题转化为单调队列

代码

class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        vector<long long> presum;
        presum.push_back(0);
        int ans = -1;
        deque<int> dq;
        dq.push_back(0);
        for (int i = 0; i < nums.size(); i++) {
            presum.push_back(presum[i] + nums[i]);

            // check front
            while (!dq.empty() && presum[i+1] - presum[dq.front()] >= k) {
                if (ans == -1) ans = i + 1 -  dq.front();
                else ans = min(ans, i + 1 - dq.front());
                dq.pop_front();
            }

            // update dq
            while (!dq.empty() && presum[dq.back()] >= presum[i+1]) {
                dq.pop_back();
            }

            dq.push_back(i+1);
        }
        return ans;
        
    }
};