862. Shortest Subarray with Sum at Least K



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



 deque

Calculate prefix sum B of list A.
B[j] - B[i] represents the sum of subarray A[i] ~ A[j-1]
Deque d will keep indexes of increasing B[i].
For every B[i], we will compare B[i] - B[d[0]] with K.

Time Complexity:
Loop on B O(N)
Every index will be pushed only once into deque. O(N)

    public int shortestSubarray(int[] A, int K) {
        int N = A.length, res = N + 1;
        int[] B = new int[N + 1];
        for (int i = 0; i < N; i++) B[i + 1] = B[i] + A[i];
        Deque<Integer> d = new ArrayDeque<>();
        for (int i = 0; i < N + 1; i++) {
            while (d.size() > 0 && B[i] - B[d.getFirst()] >=  K)
                res = Math.min(res, i - d.pollFirst());
            while (d.size() > 0 && B[i] <= B[d.getLast()]) d.pollLast();
            d.addLast(i);
        }
        return res <= N ? res : -1;
    }


FAQ
Q: How to think of such solutions?
A: 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.

Q: What is the purpose of first while loop?
A: 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.

Q: Why we pop left in the first while loop?
A: 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.

Q: What is the purpose of second while loop?
A: To keep B[D[i]] increasing in the deque.

Q: Why keep the deque increase?
A: 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.

tree map 

Using TreeMap to store prefix sum, total to record prefix sum before i,
each time total is greater or equal than K, then compare i+1 with minLen(take the minimum number),
using flooorKey(total-K) to get the prefix sum(num) which satisfy total - num >= K if num exists(which means it is null),
we compare the length with minLen(take the minimum number) and remove the num.

class Solution {
    public int shortestSubarray(int[] A, int K) {
        if (A.length == 0) return -1;
        TreeMap<Long, Integer> tree = new TreeMap<>();
        long total = 0;
        int minLen = Integer.MAX_VALUE;
        for (int i = 0; i < A.length; i++) {
            total += A[i];
            Long num = tree.floorKey(total-K);
            if (total >= K) {
                if (i+1 < minLen) {
                    minLen = i+1;
                }
            }
            while (num != null) {
                if (i-tree.get(num) < minLen) {
                    minLen = i-tree.get(num);
                }
                tree.remove(num);
                num = tree.lowerKey(num);
            }
            tree.put(total, i);
        }
        return minLen == Integer.MAX_VALUE ? -1 : minLen;
    }
}

 

posted on 2018-08-28 20:19  猪猪&#128055;  阅读(347)  评论(0)    收藏  举报

导航