从简单的窗口到维护长度固定的窗口介绍,先介绍窗口的简单作用
LCR 008.长度最小的子数组
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]
是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4] 输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0
提示:
1 <= target <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
这个问题可以先思考下,从“连续”两个字入手。既然他是找连续的长度最小的子数组,那么我们是不是可以维护一个滑动窗口,当窗口长度最小时就是答案
具体维护的办法就是定义左指针l = 0,然后右指针从0到n去滑,然后如果窗口的总和加起来大于了target,那么我们可以去更新答案。在sum - num[l]仍然大于target的时候我们就可以让左指针l往前走了
代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int ist = 0,sum = 0,ans = 0x3f3f3f3f;
for(int i = 0;i < n;i ++){
sum += nums[i];
while(ist < i && sum - nums[ist] >= target){
sum -= nums[ist ++];
}
if(sum >= target) ans = min(ans,i - ist + 1);
}
return (ans == 0x3f3f3f3f ? 0 : ans);
}
};
2516.每种字符至少取k个
给你一个由字符 'a'
、'b'
、'c'
组成的字符串 s
和一个非负整数 k
。每分钟,你可以选择取走 s
最左侧 还是 最右侧 的那个字符。
你必须取走每种字符 至少 k
个,返回需要的 最少 分钟数;如果无法取到,则返回 -1
。
示例 1:
输入:s = "aabaaaacaabc", k = 2 输出:8 解释: 从 s 的左侧取三个字符,现在共取到两个字符 'a' 、一个字符 'b' 。 从 s 的右侧取五个字符,现在共取到四个字符 'a' 、两个字符 'b' 和两个字符 'c' 。 共需要 3 + 5 = 8 分钟。 可以证明需要的最少分钟数是 8 。
示例 2:
输入:s = "a", k = 1 输出:-1 解释:无法取到一个字符 'b' 或者 'c',所以返回 -1 。
提示:
1 <= s.length <= 10^5
s
仅由字母'a'
、'b'
、'c'
组成0 <= k <= s.length
这个问题不像上道题目明确的告诉你诸如“连续”的字眼,所以要想到用窗口的话,需要把问题转化一下。
首先我们需要计算需要多少分钟能满足题目的要求,而每次拿一个字符要么从最左边要么从最右边。其实这里也是间接的告诉你做法了,就是去掉一个连续的子序列,然后最终s是否能满足条件,如果满足的话,n - max(去掉的连续子序列的长度)就是答案了,其实如果想到这里,做法基本就跟上道题差不多了
代码
class Solution {
public:
int takeCharacters(string s, int k) {
if(k == 0) return 0;
unordered_map<char,int> hs;
int n = s.size();
for(auto ch : s) hs[ch] ++;
if(hs.size() != 3) return -1;
for(auto x : hs) if(x.second < k) return -1;
int ist = 0,res = -1;
for(int i = 0;i < n;i ++){
hs[s[i]] --;
while(hs[s[i]] < k){
hs[s[ist]] ++;
ist ++;
}
res = max(res,i - ist + 1);
}
return n - res;
}
};
最后介绍一下如果窗口长度是定值的话,怎么维护这个窗口,其实就是单调队列
239.滑动窗口最大值
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
提示:
1 <= nums.length <= 10^5
-104 <= nums[i] <= 10^4
1 <= k <= nums.length
讲解的话在这里 洛谷 P1886 滑动窗口 /【模板】单调队列 (自己造轮子和STL的deque)-优快云博客
代码
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> q;
vector<int> ans;
for(int i = 0;i < nums.size();i ++){
while(!q.empty() && i - k + 1 > q.front()) q.pop_front();
while(!q.empty() && nums[q.back()] < nums[i]) q.pop_back();
q.push_back(i);
if(i >= k - 1) ans.push_back(nums[q.front()]);
}
return ans;
}
};
加油