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 ^ 5
1 <= K <= 10 ^ 9
思路
本來以為用dp來做,看了下答案,解法中並沒有。使用的解法是滑動窗口,將問題重新定義為和A的前綴和有關,定義
P[i] = A[0] + A[1] + ... + A[i-1]
我們想要求的便是最小的 y-x,y>x 並且P[y] - P[x] >= K
Motivated by that equation, let opt(y)
be the largest x
such that P[x] <= P[y] - K
. We need two key observations:
-
If
x1 < x2
andP[x2] <= P[x1]
, thenopt(y)
can never bex1
, as ifP[x1] <= P[y] - K
, thenP[x2] <= P[x1] <= P[y] - K
buty - x2
is smaller. This implies that our candidatesx
foropt(y)
will have increasing values ofP[x]
. -
If
opt(y1) = x
, then we do not need to consider thisx
again. For if we find somey2 > y1
withopt(y2) = x
, then it represents an answer ofy2 - x
which is worse (larger) thany1 - x
.
opt(y)是使得當 P[x] <= P[y] - K 時 x 能取到的最大值。
1. 如果有 x1<x2 並且 P[x2]<=P[x1],那么opt(y)一定不是 x1,因為如果有P[x1] <= P[y] - K,那么 P[x2] <= P[x1] <= P[y] - K,但是 y - x2
is smaller。這表明對於opt(y)的候選x應該是在使P(x)遞增的區間去找。要注意這里的P[x1]指的是從0到X1的數組元素之和,不是單單指一個x1位置上元素的值。
2. 如果opt(y1)=x, 那么不需要再次考慮x。因為如果我們找到某些y2>y1並且opt(y2)=x,那么這表明這個解答 y2-x 是比之前的解答 y1-x 是更壞的答案。
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
.
public int shortestSubarray(int[] A, int K) { int N = A.length, res = N + 1; int[] B = new int[N + 1]; // 下面利用數組A重新構造了數組B,滿足B[i+1]-B[j]=A[i]+A[i-1].....+A[j]
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(); // Deque d keep indexes of increasing B[i]
d.addLast(i); } return res <= N ? res : -1; }
上面的出入隊列順序是這樣的:首先對於每個索引i,對應的是B[i],將這個索引作為y位置來考慮,因為雙端隊列保持的索引是的B[i]是遞增的,為了從最大處逼近K,我們從隊頭依次取索引出來計算:
B[i] - B[d.getFirst()]
如果比K大,那么則要找這其中距離索引i最近的那一個:
res = Math.min(res, i - d.pollFirst());
然后是隊列要keep indexes of increasing B[i],索引判斷當前的B[i]是否大於隊列尾部的索引處的
B[i] <= B[d.getLast()
如果不能構成遞增,根據之前的分析,當前y所在的位置i的最優解opt(y)一定不會是在前面遞增的部分取,所以隊列要從后往前一個個彈出隊尾直至能和B[i]構成遞增序列。