LeetCode862 Shortest Subarray with Sum at Least K(not fully understand)
we use to solve the problem of get the shortest subarray with sum of K, and we can use two pointer to solve this problem. and we can also use the binary search to solve that problem.
but how do we solve this problem?
this problem can be solved in monotonic queue, like we used to solve similar problem using monotonic stack.
and in a more general way to say, this is a dp related problem and this is also a sliding max/min windows problem.
which means, any dp problem with A[i] = min(A[j:k]) + C where j < k <= i can be solve in monotonic queue.
so how does this problem applied to the monotonic queue?
well, this problem aims to return the length of the shortest subarray of A with sum at least K.
so let’s assume B is the accumulate array A. so which means we need to find the closest l~r which sum(A[l:r]) >= K, so => B[r] - K >= B[l], so if B[l] is fixed, then we need to find the closest r, than B[r] is larger than B[l] with K or more.
so this is a monotonic increasing queue and we need to make sure that only increasing larger or equals to K will be added in queue.
class Solution {
public int shortestSubarray(int[] A, int K) {
int N = A.length, res = N + 1; //res is the global shortest length
int[] B = new int[N + 1]; //B is the culmutive array
for (int i = 0; i < N; i++) B[i + 1] = B[i] + A[i];
Deque<Integer> d = new ArrayDeque<>(); //due to we need to find the shortest length, so we store index using this deque
for (int i = 0; i < N + 1; i++) {
while (d.size() > 0 && B[i] - B[d.getFirst()] >= K) //only larger or equals to K
res = Math.min(res, i - d.pollFirst()); //keep poll first until
while (d.size() > 0 && B[i] <= B[d.getLast()]) //this make sure the deque is incresing
d.pollLast(); //everything that is not inreasing is polled
d.addLast(i); //then we add i into deque
}
return res <= N ? res : -1;
}
}
and the next five Q&A answers every problem in this code.
How to think of such solutions?
Basic idea, for array starting at every A[i], find the shortest one with sum at leat K.
In my solution, for B[i], find the smallest j that B[j] - B[i] >= K.
Keep this in mind for understanding two while loops.
What is the purpose of first while loop?
For the current prefix sum B[i], it covers all subarray ending at A[i-1].
We want know if there is a subarray, which starts from an index, ends at A[i-1] and has at least sum K.
So we start to compare B[i] with the smallest prefix sum in our deque, which is B[D[0]], hoping that [i] - B[d[0]] >= K.
So if B[i] - B[d[0]] >= K, we can update our result res = min(res, i - d.popleft()).
The while loop helps compare one by one, until this condition isn’t valid anymore.
Why we pop left in the first while loop?
This the most tricky part that improve my solution to get only O(N).
D[0] exists in our deque, it means that before B[i], we didn’t find a subarray whose sum at least K.
B[i] is the first prefix sum that valid this condition.
In other words, A[D[0]] ~ A[i-1] is the shortest subarray starting at A[D[0]] with sum at least K.
We have already find it for A[D[0]] and it can’t be shorter, so we can drop it from our deque.
What is the purpose of second while loop?
To keep B[D[i]] increasing in the deque.
Why keep the deque increase?
If B[i] <= B[d.back()] and moreover we already know that i > d.back(), it means that compared with d.back(),
B[i] can help us make the subarray length shorter and sum bigger. So no need to keep d.back() in our deque.

浙公网安备 33010602011771号