Difficulty:Hard
Total Accepted:4.9K
Total Submissions:25.6K
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
解决思路:
最开始的做法是两个循环,将n(n-1)/2个子序列组合遍历一遍
时间复杂度为o(n^2)
class Solution {
public:
int shortestSubarray(vector& A, int K) {
long int result = 100000;
int length = A.size();
long sum = 0;
bool isFind = false;
for (int i = 0; i < length; i++) {
sum = 0;
for (int j = i; j < length; j++) {
sum += A[j];
if (sum >= K) {
result = min(result, (long int)(j - i + 1));
isFind = true;
break;
}
}
}
if (!isFind) {
return -1;
}
return result;
}
};
很不幸,果然遇到后面的大数据,长度有三四万,处理时间超时了
后来参考外国网友的思路,进行改善:
Same idea, this is my thinking process to improve the O(N^2) solution to O(N).
public int shortestSubarray(int[] A, int K) {
int[] presum = new int[A.length+1];
int res = A.length+1;
for(int i=1;i=0;j--){
if(presum[i]-presum[j]>=K)
res = Math.min(res, i-j);
}
}
return res == (A.length+1)?-1: res;
}
This is a O(n^2) solution ( Time Limit Exceeded ).
If we have an array prefix sum array like [0,1,2,3,4,10,7,4,5,14,16,… ], K=11
The best answer is: length=2 (i=8, j=10).
For the subarray [4,10,7,4], do we need the first three elements 4,10,7? We don’t, if we have presum[j]-10>=K, presum[j]-4(later one) is also larger than K and has a shorter length. Therefore, the elements we want to use in the inner loop are actually like [0,1,2,3,**4(later one),**5,14,16, …]. When we visit later 4 in the outer loop, we can remove previous elements that are larger than 4. What we get will be a ascent array.
The first answer we found will be length=6 (i=3, j=9). After we find this answer, do we still need the elements 0,1,2? We don’t. Since the next answer must be shorter than this one, we don’t need to compare later element with these elements. Now, the array will be [3, 4, 5, 14, 16, …]
Then, when we visit 16 in the outer loop. And we will sequentially visit and remove 3,4 in the inner loop. Finally, we get the best answer (i=8, j=10).
Okay, there are two steps we need to reduce redundant calculations.
Remove previous elements that are larger than the current one ( second while loop ).
If we find an answer (i, j), remove smaller elements than presum[i]. Since we have keep elements in ascent order with the first step, we can just remove previous elements before i ( fisrt while loop ).
Since we only need to remove head or tail elements, Deque will be a great data structure.
添加两个循环,一个用来将局部和为负数的部分去掉,另一个循环用来将在同样符合条件的子序列中将更远更长的那个头下标去掉,再利用双向队列的特性,将时间复杂度从o(n^2)缩短为o(n),因为每个B[i]只需要插入一次
下面是击败了99.85%提交的代码:
class Solution {
public:
int shortestSubarray(vector& A, int K) {
int length = A.size();
int result = length + 1;
deque d;
vector B(length+1, 0);//B[i]就是A的前i个数的和
for (int i = 1; i <= length; i++) {
B[i] = B[i - 1] + A[i-1];
}
for (int i = 0; i <= length; i++) {
while (d.size() && (B[i] - B[d.front()]) >= K) {
result = min(result, (i - d.front()));
d.pop_front();
}
while (d.size() && B[i] <= B[d.back()]) {
d.pop_back();
}
d.push_back(i);
}
if (result==(length+1)) {
return -1;
}
return result;
}
};
static const auto io_sync_off = []()
{
// turn off sync
std::ios::sync_with_stdio(false);
// untie in/out streams
std::cin.tie(nullptr);
return nullptr;
}();