这道题是一道挺有意思的题,也有一点难度。下面给出题目地址:
https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/
这道题的大意是给定一个整数序列,求一个最短的连续非空的子序列,使它的和至少为K,返回它的长度。
这道题是一道对数字序列操作的题,而且是求连续子序列的和的问题。这种题的一个很自然的处理方法就是将序列中第一个数到第i个数的和存成数组sum。这样,任何一个连续子序列就可以用数组中两个数的差求出来。但是,很显然,只有这样处理是不行的。通过暴力循环,我们可以看出算法复杂度为O(n^2),而数组长度最大为50000,并不能满足。
后来看了一下别人的算法。发现它用了一种很特别的处理方法,就是关注第r个数结尾的最短的子序列,然后遍历r。这种倒过来保存的方法似乎比较适合滑动窗口。在遍历r的时候,我们知道这个子序列是以r结尾的,所以我们要找到一个最大的开头j,使j到r的子序列的和大于k。然后我们维护一个双端队列,队列中保存子序列可能的开头序号。在这里,我们先要明确双端队列中的两个数当i<j且sum[i]>sum[j],那么选择j作为子序列开头肯定是更好的解。因为,子序列更短而且差值更大。所以队列中的序号i是递增的,它们对应的sum[i]也是递增的 。
我们每遍历一次r,就应该往队列中插入r-1,就是前一个数。可以比较队列末尾的数值不值得保留。就是对比是否sum[j]<sum[r-1]。否则就从尾端删除,然后再往前遍历。这个可以参见LIS的O(nlog(n))算法。有一道类似的题目,是Uva1471,defense lines。这个处理方法类似。
下面给出实现代码(下面这个是用list实现双端队列的版本):
class Solution {
public:
list<int> slide;
int shortestSubarray(vector<int>& A, int K) {
int i, last, ans = 100000, index;
vector<int> sum;
sum.push_back(0);
for(i=0; i<A.size(); i++)
{
sum.push_back(sum[i]+A[i]);
}
for(i=1; i<sum.size(); i++)
{
last = max(0, i - ans);
while(!slide.empty())
{
index = slide.back();
if(sum[index]>=sum[i-1])
slide.pop_back();
else
break;
}
slide.push_back(i-1);
if(sum[i]<sum[i-1])
continue;
for(list<int>::iterator it=slide.begin();it!=slide.end();)
{
index = *it;
if(index<last)
{
it++;
slide.pop_front();
continue;
}
else if(sum[i]-sum[index]>=K)
ans = i - index;
else
break;
it++;
}
}
if(ans == 100000)
ans = -1;
return ans;
}
};
结果:
滑动数组实现双端队列法:
int slide[50010];
class Solution {
public:
int shortestSubarray(vector<int>& A, int K) {
int i, last, ans = 100000, index, fro = 0, bac = -1;
vector<int> sum;
sum.push_back(0);
for(i=0; i<A.size(); i++)
{
sum.push_back(sum[i]+A[i]);
}
for(i=1; i<sum.size(); i++)
{
while(fro<=bac)
{
index = slide[bac];
if(sum[index]>=sum[i-1])
bac--;
else
break;
}
last = max(0, i - ans + 1);
bac++;
slide[bac] = i-1;
if(sum[i]>sum[i-1])
{
int cnt = fro;
while(cnt<=bac)
{
index = slide[cnt];
if(index<last)
fro++;
else if(sum[i]-sum[index]>=K)
ans = i - index;
else
break;
cnt++;
}
}
}
if(ans == 100000)
ans = -1;
return ans;
}
};
结果: