题解 | 和大于等于K的最短子数组(单调栈+二分)
和大于等于K的最短子数组
http://www.nowcoder.com/practice/3e1fd3d19fb0479d94652d49c7e1ead1
双指针(错误)
写完双指针,发现题目输入还有负数,感觉双指针不对......
想到这个数据 [-10, 3,-10],3,会输出-1。
(错误代码)
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
int n = nums.size(), ans = INT_MAX;
long long cnt = 0;
for (int i = 0, j = 0; i < n; ++i) {
cnt += nums[i];
while (cnt >= k) {
cnt -= nums[j ++];
}
if (j) ans = min(ans, i - j + 2);
}
return ans == INT_MAX ? -1 : ans;
}
};
单调栈+二分
(这个做法应该是对的...)
子数组和 ,即 ( 为前缀和)。
枚举 ,应该选择尽量大的 满足上面不等式。
假设找到一个 ,在 的左边有一个 , , 比 k更好。
因为 : ①、 如果能满足上面不等式, 也一定能满足,而且形成的子数组, 的比 更短。②、如果 能满足上面不等式, 不一定能满足,因为。
因此前面更大的数是没用的,使用单调栈维护递增序列。枚举,二分单调栈找到最大的。
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
using LL = long long;
int n = nums.size();
vector<LL> pre(n + 1);
vector<int> stk(n + 1);
int top = 1, ans = INT_MAX;
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] + nums[i - 1];
}
for (int i = 1; i <= n; ++i) {
if (pre[i] - pre[stk[1]] >= k) {
int l = 1, r = top;
while (l < r) {
int mid = l + r + 1 >> 1;
if (pre[i] - pre[stk[mid]] >= k) {
l = mid;
} else {
r = mid - 1;
}
}
ans = min(ans, i - stk[r]);
}
while (top && pre[i] <= pre[stk[top]]) --top;
stk[++top] = i;
}
return ans == INT_MAX ? -1 : ans;
}
};