好久没有更新了,这几天主要对hard级别的题目做了一些,今天分享一下862题的解题思路,一般看到这种类型的题目,有两个反应,暴力遍历所有情况,然后选择出最好的,但是这种情况肯定是行不通的,效率是非常低的,而且这种采用加和形式的题目,如果前面加出来的结果不能被后面的计算步骤利用,那将不是一个比较好的结果,因此,在看了大神们的代码后,整理了一下思路。
为了实现O(N)的时间复杂度,我们可以从两个点入手:
第一,当出现负数的时候,该点一定不能作为子序列的起始点,因为不论当前的子序列的和是小于目标值还是大于目标值,该点都不能作为起始点。如果目前的和已经大于目标值,则该点后面的点一定符合条件(因为负数对和是有消极作用的),如果目前的数小于目标值,起始点要么是前面某个正数,要么是后面某个数值,所以,负数出现的点一定不能作为满足条件的子序列起始点。
第二点,若此时找到了满足情况的点,left(起点),right(终点),此时,如果我们需要更小的满足条件的其他序列,就不能在使用这个left作为起点,因为如果往后出现更短的满足条件的子序列,起点一定在该点之后。
以上两点是整个算法的核心所在,另外,我们首先用一个序列表示前几个元素的和,这样,任何子序列的和都能通过该序列的两个元素相减得到,方便整个操作,本人不习惯于用公式推到,如果语言描述上有不清楚的地方,请大家说明。
具体的代码见下:
class Solution {
public:
int shortestSubarray(vector<int>& A, int K) {
if (A.size() < 1) {
return -1;
}
int length = -1;
vector<int> allsum (A.size()+1, 0);
for (int i=1; i<allsum.size(); ++i) {
allsum[i] = allsum[i-1]+A[i-1];
}
deque<int> dp;
for (int right=0; right < allsum.size(); ++right) {
while (!dp.empty() && allsum[right] <= allsum[dp.back()]) {
dp.pop_back();
}
while (!dp.empty() && allsum[right]-allsum[dp.front()] >= K) {
int left = dp.front();
dp.pop_front();
if (length == -1) {
length = right-left;
} else {
length = min(length, right-left);
}
}
dp.push_back(right);
}
return length;
}
};