862. Shortest Subarray with Sum at Least K #
Problem #
Return the length of the shortest, non-empty, contiguous subarray of A with sum at least K.
If there is no non-empty subarray with sum at least K, return -1.
Example 1:
Input: A = [1], K = 1
Output: 1
Example 2:
Input: A = [1,2], K = 4
Output: -1
Example 3:
Input: A = [2,-1,2], K = 3
Output: 3
Note:
1 <= A.length <= 50000-10 ^ 5 <= A[i] <= 10 ^ 51 <= K <= 10 ^ 9
Problem Summary #
Return the length of the shortest non-empty contiguous subarray of A whose sum is at least K. If there is no non-empty subarray with sum at least K, return -1.
Notes:
- 1 <= A.length <= 50000
- -10 ^ 5 <= A[i] <= 10 ^ 5
- 1 <= K <= 10 ^ 9
Solution Approach #
- Given an array, find the shortest, non-empty, contiguous subsequence whose cumulative sum is at least k.
- Since the given array may contain negative numbers, the cumulative sum of a subarray will not necessarily increase as the length of the array increases. The problem asks for interval sums, so naturally we need to use the
prefixSumprefix sum. First compute theprefixSumprefix sum. - Simplifying the requirement of the problem, it is whether we can find
prefixSum[y] - prefixSum[x] ≥ K, with the differencey - xminimized. Ifyis fixed, then forx, the largerxis, the smaller the differencey - xis (becausexgets closer toy). Therefore, to find the shortest distance of the interval[x, y], we need to ensure thatyis as small as possible andxis as large as possible, so that the distance of the interval[x, y]is minimized. Then the following 2 pieces of “common sense” must hold:- If
x1 < x2, andprefixSum[x2] ≤ prefixSum[x1], it means the result will definitely not choosex1. Because ifprefixSum[x1] ≤ prefixSum[y] - k, thenprefixSum[x2] ≤ prefixSum[x1] ≤ prefixSum[y] - k, sox2can also satisfy the problem requirements, andx2is closer toythanx1; the optimal solution will definitely considerx2first. - After
xhas been determined, there is no need to considerxagain later, because ify2 > y1, and when aty2choosingxis still the same, then when calculating the distance,y2 - xis obviously greater thany1 - x; in this case it definitely will not be the shortest distance.
- If
- From the two pieces of common sense above, we can use a double-ended queue
dequeto processprefixSum. What is stored indequeare increasingxindices, in order to satisfy the first piece of common sense. Start traversing from the front of the double-ended queue. If the difference of the interval sums is greater than or equal toK, remove the front element and update the resultres. Removing elements from the front of the queue until the inequalityprefixSum[i]-prefixSum[deque[0]] >= Kis no longer satisfied is to satisfy the second piece of common sense. The following loop is the essence of this problem: traverse backward from the end of the double-ended queue. If the current interval sumprefixSum[i]is less than or equal to the interval sum at the end of the queue, remove the element at the end of the queue. Why handle it this way? Because if the array is all positive numbers, then the longer the length, the larger the interval sum must be, soprefixSum[i]must be greater than all interval sums in the double-ended queue. However, because negative numbers may exist, the length may become longer while the total interval sum instead decreases. The previous differences of interval sums were not greater than or equal toK(< K), so the current one is even less likely to be greater than or equal toK; this ending position can be eliminated directly without calculation. After the loop ends, add the current position to the double-ended queue. Removing several elements from the tail when encountering a new index is also to satisfy the first piece of common sense. - Since every index enters the queue only once and is popped out at most once, the time complexity is O(n), and the space complexity is O(n).
Code #
func shortestSubarray(A []int, K int) int {
res, prefixSum := len(A)+1, make([]int, len(A)+1)
for i := 0; i < len(A); i++ {
prefixSum[i+1] = prefixSum[i] + A[i]
}
// deque stores prefixSum indices in increasing order
deque := []int{}
for i := range prefixSum {
// The following loop tries to find a cumulative sum >= K in the interval [deque[0], i]; if found, update the answer
for len(deque) > 0 && prefixSum[i]-prefixSum[deque[0]] >= K {
length := i - deque[0]
if res > length {
res = length
}
// After finding the first deque[0] that satisfies the condition, remove it because it is already the shortest-length subarray
deque = deque[1:]
}
// The following loop ensures that prefixSum[deque[i]] is increasing
for len(deque) > 0 && prefixSum[i] <= prefixSum[deque[len(deque)-1]] {
deque = deque[:len(deque)-1]
}
deque = append(deque, i)
}
if res <= len(A) {
return res
}
return -1
}