Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
ps:前几天虾皮面试的时候问了这个题。
题目描述
难度:Hard
标签:单调队列
一个长度为 数组,有正有负,求一个最短的子数组满足 ,输出长度。
数据范围:
测试用例:
输入: nums = [1], k = 1
输出: 1
输入: nums = [1,2], k = 4
输出: -1
输入: nums = [2,-1,2], k = 3
输出: 3
思路分析
做法1-标准解法:单调队列
首先我们知道子区间问题,要么动态规划,要么滑动窗口,要么前缀和,这个题目是相加,但动态规划和滑窗似乎不太好做。所以考虑利用前缀和的情况下如何去处理。先预处理完了前缀和,题目就变成了:当前值是 ,需要在 之间找到一个值 满足 ,也就是:,仔细考察一下这个题的性质,会发现:
- 假设当前下标是 ,存在一个前缀下标 满足要求,就可以更新一下答案,并且在 之前的所有值都可以不用再管了,因为后续更新答案的时候如果选他们,长度一定比现在 大。 这也就是说,从头部出队,找到满足条件的值中最大的下标,前面小于该下标的值都可以不用管了。
- 考虑入队一个值时,如果现在这个值比队尾的小,那说明以后都不可能考虑那个元素了,因为它太大了而且下标还早于当前值,后面更新答案的时候就是选 也不可能选它,所以那个值要出队了。(有一个后辈既比你强又比你年轻,你不就没用可以直接被踢了么)
到这里已经很明显了,单调队列完美满足这种需求。
AC 代码(单调队列)
using ll = long long;
class Solution {
constexpr static int INF = 0x3f3f3f3f;
public:
int shortestSubarray(vector<int>& nums, int k) {
vector<ll> pref{0};
for (int i : nums) {
pref.push_back(pref.back() + i);
}
int n = size(pref);
int ans = INF;
deque<int> q;
for (int i = 0; i < n; i++) {
while (!q.empty() and pref[i] - pref[q.front()] >= k) {
ans = min(ans, i - q.front());
q.pop_front();
}
while (!q.empty() and pref[i] < pref[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
return ans == INF ? -1 : ans;
}
};
做法2-离散化+树状数组 维护前缀区间内满足条件的最大值
在不知道前面的性质,分析不出来用的是单调队列的情况下,我们拿到前缀和数组 后,知道对应 ,需要找的是 区间内一个最大的下标 满足 ,也就是求一个 ,满足: 。
这个操作可以通过树状数组做,但是因为 和 的值不是连续的,所以需要进行离散化,把所有的 和 离散到坐标 内。
离散化完成后,用树状数组保存前缀区间内,满足小于等于 x 的值中所有坐标中的最大值
,也就是说:用树状数组的 代表变成 小于等于 i 的数中最大的那个坐标
。这样,对于每个 ,需要查询的就是 ,这个值代表了满足条件的最大下标,那么就可以更新 了.
AC 代码(树状数组实现)O(nlogn)
using ll = long long;
constexpr static int INF = 0x3f3f3f3f;
struct BIT {
int n;
vector<int> tree;
BIT(int n) {
this->n = n;
tree = vector<int>(n, -1);
}
int lowbit(int x) {
return x & (-x);
}
void insert(int id, int x) {
for (int i = id; i < n; i += lowbit(i)) {
tree[i] = max(x, tree[i]);
}
}
int query(int id) {
int ans = -1;
for (int i = id; i > 0; i -= lowbit(i)) {
ans = max(ans, tree[i]);
}
return ans;
}
};
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
vector<ll> pre{0};
for (int i = 0; i < size(nums); i++) {
pre.push_back(pre.back() + nums[i]);
}
vector<ll> allNums;
for (auto i : pre) {
allNums.push_back(i);
allNums.push_back(i - k);
}
sort(begin(allNums), end(allNums));
unordered_map<int, int> ids;
int cnt = 1;
for (auto i : allNums) {
if (!ids.count(i)) ids[i] = cnt++;
}
BIT tree(cnt + 1);
int ans = INF;
for (int i = 0; i < size(pre); i++) {
int maxId = tree.query(ids[pre[i] - k]);
if (maxId != -1) {
ans = min(ans, i - maxId);
}
tree.insert(ids[pre[i]], i);
}
return ans == INF ? -1 : ans;
}
};