Slide Window 专题
209. Minimum Size Subarray Sum
给定正整数数组和正整数s,找到加和大于等于s的连续子数组的最小长度。
基础slide window题目。
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int ret = INT_MAX, win_size = , len = nums.size(), sum = ;
for(int i=; i<len; i++){
sum += nums[i];
win_size++;
while(sum >= s && win_size > ){
ret = min(ret, win_size);
sum -= nums[i-win_size+];
win_size--;
}
}
return ret==INT_MAX ? : ret;
}
};
862. Shortest Subarray with Sum at Least K
209加强版,与上一题唯一不同的地方在于,数组中存在负数。
Detailed intuition behind deque solution 这份题解写得特别好。其中就上一题209讲解了滑动窗口算法工作的基本原理:
Incremeting the end pointer while the sum of current subarray (defined by current values of
start
andend
) is smaller than the target.Once we satisfy
our condition (the sum of current subarray >= target) we keepincrementing
the start pointer until weviolate
it (untilsum(array[start:end+1]) < target
).Once we violate the condition we keep incrementing the end pointer until the condition is satisfied again and so on.
也讲解了为什么在有负数的情况下,传统滑动窗口算法不能工作:
The problem with this solution is that it doesn't work if we have negative values, this is because of the sentence above Once we "violate" the condition we stop incrementing start
.
同时举例说明:
Now, let's take an example with negative values nums = [3, -2, 5]
and target=4
. Initially start=0
, we keep moving the end pointer until we satisfy the condition, here we will have start=0
and end=2
. Now we are going to move the start pointer start=1
. The sum of the current subarray is -2+5=3 < 4
so we violate the condition. However if we just move the start pointer another time start=2
we will find 5 >= 4
and we are satisfying the condition. And this is not what the Sliding window assumes.
引出deque改进的滑动窗口算法:
What does the Deque store :
deque保存start指针可能的值,因为deque需要保证单调递增,所以不一定是连续。
The deque stores the possible
values of the start pointer. Unlike the sliding window, values of the start
variable will not necessarily be contiguous.
Why is it increasing :
之所以维护递增的队列,是为了保证,当d[0]不满足条件时,d[i] i>0 都不满足条件。这样一来才能适用于滑动窗口算法。
So that when we move the start pointer and we violate the condition, we are sure we will violate it if we keep taking the other values from the Deque. In other words, if the sum of the subarray from start=first value in the deque
to end
is smaller than target
, then the sum of the subarray from start=second value in the deque
to end
is necessarily smaller than target
.
So because the Deque is increasing (B[d[0]] <= B[d[1]]
), we have B[i] - B[d[0]] >= B[i] - B[d[1]]
, which means the sum of the subarray starting from d[0]
is greater than the sum of the sub array starting from d[1]
.
Why do we have a prefix array and not just the initial array like in sliding window :
由于deque里的值不是连续的,所以不能像传统滑动窗口(start指针的取值是连续变化)那样仅通过一个sum来维护每个窗口的和。因此需要维护前缀和来求得每个窗口的和。
Because in the sliding window when we move start
(typically when we increment it) we can just substract nums[start-1]
from the current sum and we get the sum of the new subarray. Here the value of the start
is jumping
and one way to compute the sum of the current subarray in a constant
time is to have the prefix array.
Why using Deque and not simply an array :
既然需要从start端取数,又需要从end端取数,还需要从end端插入数,因此使用deque。
We can use an array, however we will find ourselves doing only three operations:
1- remove_front
: when we satisfy our condition and we want to move the start pointer
2- append_back
: for any index that may be a future start pointer
3- remove_back
: When we are no longer satisfying the increasing order of the array
Deque enables doing these 3 operations in a constant time.
解法:
首先计算nums的前缀和P。对于每个下标有y,我们希望找到opt(y),opt(y)是最大的 x(x<y),使得 P[y]-P[x] >= k。
- 若存在 x2>x1 ,且P[x2] <P[x1],那么opt(y)一定不是x1,因为 P[y] - P[x2] >= P[y] - P[x1],且 y - x2 < y - x1
- 若已存在opt(y1)==x,那么x就不用再被考虑,因为若存在y2 > y1,opt(y2)==x,y2-x > y1-x
维护一个存有P下标的单调队列,当把idx入到队尾前,需将队尾满足P[tail] > P[idx]的tail出队。
若对头head,满足P[y]-P[head] >= k,则将head弹出。
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k){
int len = nums.size(), result = len+;
vector<long long> prefix(len+, );
for(int i=; i<=len; i++)
prefix[i] = prefix[i-] + nums[i-];
deque<int> mono_q;
mono_q.push_back();
for(int i=; i<=len; i++){
while(!mono_q.empty() && prefix[i] - prefix[mono_q.front()] >= k){
result = min(result, i - mono_q.front());
mono_q.pop_front();
}
while(!mono_q.empty() && prefix[mono_q.back()] >= prefix[i])
mono_q.pop_back();
mono_q.push_back(i);
}
return result==len+ ? - : result;
}
};
992. Subarrays with K Different Integers
给定一个正整数数组,计算刚好有K个不同数的子数组的个数。(For example, [1,2,3,1,2]
has 3
different integers: 1
, 2
, and 3
.)
解法一:slide window
如果是求最多有K个不同元素的子数组,那么就是典型的slide window的题目,只需要在这个典型题目上增添一步:
exactly(K) = atMost(K) - atMost(K-1)
class Solution {
public:
int subarraysWithKDistinct(vector<int>& A, int K) {
//cout<<atMost(A, K)<<" "<<atMost(A, K-1)<<endl;
return atMost(A, K) - atMost(A, K-);
} int atMost(vector<int> &A, int K){
map<int, int> count;
int ret = , win_size = , len = A.size(), ctr = ;
for(int i=; i<len; i++){
if(count[A[i]] == ){
ctr++;
}
count[A[i]]++;
while(ctr > K){
if((--count[A[i-win_size--]]) == )
ctr--;
}
++win_size;
//cout<<i<<" "<<win_size<<endl;
ret += (win_size);
}
return ret;
}
};
解法二:prefix slide window
思路:
如果子数组[j, i]包含K个不同元素,并且前prefix个元素也出现在子数组[j+prefix, i]中,那么可以得到1+prefix个符合要求的子数组。例如,[1, 2, 1, 2, 3],前两个数[1, 2]也出现在子数组[1,2,3]中,可以得到1+2
个符合要求的子数组,[1, 2, 1, 2, 3]
, [2, 1, 2, 3]
和 [1, 2, 3]
.
遍历数组,维护滑动窗口,窗口尾指向当前元素,窗口头head移动至j,使A[j]在窗口中只出现一次。换句话说,在保证不同元素数不变的情况下,尽量缩短窗口。为达到这个目的,对出现在窗口中元素进行计数,当下一个元素添加到窗口尾时,从窗口头移除尽量多的元素,直至窗口头指向的元素仅在窗口中出现一次,在移除元素的同时,递增prefix。
如果窗口中存在K个不同元素,可以得到1+prefix个符合要求的子数组。
如果窗口中有K+1个不同元素,我们需要移除窗口头指向的元素(该元素仅出现在窗口头),因为我们开始计算一组新的子数组所以重置prefix。
class Solution {
public:
int subarraysWithKDistinct(vector<int>& A, int K) {
int len = A.size(), prefix = , head = , ret = ;
map<int, int> count;
for(int i=; i<len; i++){
if(!count[A[i]]++)
K--;
if(K < ){
--count[A[head++]];
prefix = ;
K++;
}
while(count[A[head]] > ){
--count[A[head++]];
prefix++;
}
if(K==)
ret += prefix+;
}
return ret;
}
};
类似题目:
1248. Count Number of Nice Subarrays
解法一:slide window
exactly(K) = atMost(K) - atMost(K-1)
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
return atMost(nums, k) - atMost(nums, k-);
}
int atMost(vector<int> &nums, int k){
int len = nums.size(), ret = , head = ;
for(int i=; i<len;i++){
if(nums[i]%)
k--;
while(k<){
if(nums[head++]%)
k++;
}
ret += i-head+;
}
return ret;
}
};
解法二:prefix slide window
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int len = nums.size(), head = , ret = , prefix = ;
for(int i=; i<len; i++){
if(nums[i]%)
k--;
if(k<){
head++;
k++;
prefix = ;
}
while(head<=i && nums[head]%==){
head++;
prefix++;
}
if(k==)
ret += prefix+;
//cout<<k<<" "<<prefix<<endl;
}
return ret;
}
};
395. Longest Substring with At Least K Repeating Characters
求字符串s的子串的最大长度,该子串要求所有字符在该子串中出现的次数都至少是k次。
解法一:滑动窗口
一开始把题目看成了,子串中每个字符至多出现k次。如果是这样,那么是一道典型的滑动窗口的题目。
然而,题目是至少出现k次。这样一来,滑动窗口不再适用。因为,在字符出现至多k次的问题中,当窗口尾部的字符超过k个,意味着只需要将窗口头部往后移动直至尾部字符等于k个;当尾部字符出现次数小于k次时,只需要将窗口尾部继续往后移动即可。而在字符出现至少k次的问题中,当尾部字符出现次数小于k次时,无法判断是移动窗口头部(舍弃尾部当前字符,后面可能不再有窗口尾部的字符)还是移动窗口尾部(期待后面还有更多当前尾部字符)。
在此题中,字符出现的次数不能用来决定窗口的动作(移动头还是移动尾)。但是可以引入别的依据来移动窗口,例如,窗口中不同字符至多多少个。问题从至少转换为至多。那转换后的问题与原问题的关系是什么呢?窗口依照其中出现的不同字符数量移动,我们对窗口中的不同字符(unique)和出现次数大于等于k的字符(at_least_k)进行计数,若对当前窗口unique==at_least_k,说明该窗口中所有不同的字符都至少出现了k次。
class Solution {
public:
int longestSubstring(string s, int k) {
int result = ;
for(int i=; i<=; i++){
int unique = , at_least_k = , win_size = , len = s.size();
unordered_map<char, int> ctr;
for(int j=; j<len; j++){
if(ctr[s[j]]++ == )
unique++;
if(ctr[s[j]] == k)
at_least_k++;
while(unique > i){
if(ctr[s[j-win_size]] == k)
at_least_k--;
if(--ctr[s[j-win_size--]] == )
unique--;
}
win_size++;
if(unique == at_least_k)
result = max(result, win_size);
}
}
return result;
}
};
解法二:分治法 https://www.cnblogs.com/jasonlixuetao/p/11945760.html
Detailed intuition behind Deque solution
Slide Window 专题的更多相关文章
- Siddhi CEP Window机制
https://docs.wso2.com/display/CEP400/SiddhiQL+Guide+3.0#SiddhiQLGuide3.0-Window https://docs.wso2.co ...
- [LeetCode] 76. Minimum Window Substring 解题思路
Given a string S and a string T, find the minimum window in S which will contain all the characters ...
- sliding window:"Marginalization","Schur complement","First estimate jacobin"
[1]知行合一2 SLAM中的marginalization 和 Schur complement SLAM的Bundle Adjustment上,随着时间的推移,路标特征点(landmark)和相机 ...
- EasyPR--开发详解(8)文字定位
今天我们来介绍车牌定位中的一种新方法--文字定位方法(MSER),包括其主要设计思想与实现.接着我们会介绍一下EasyPR v1.5-beta版本中带来的几项改动. 一. 文字定位法 在EasyPR前 ...
- c++堆
c++ reference: http://www.cplusplus.com/reference/algorithm/make_heap/ heap并不属于STL容器组件,它分为 max heap ...
- jquery之右下角消息提示框
messager.js (function (jQuery) { var window; var obj = new Object(); obj.version = '@1.0'; obj.title ...
- [LeetCode] 3. Longest Substring Without Repeating Characters 解题思路
Given a string, find the length of the longest substring without repeating characters. For example, ...
- [LeetCode] Minimum Size Subarray Sum 解题思路
Given an array of n positive integers and a positive integer s, find the minimal length of a subarra ...
- [LeetCode#159] Missing Ranges Strobogrammatic Number
Problem: Given a string, find the length of the longest substring T that contains at most 2 distinct ...
随机推荐
- dependencies 和 starter
以 spring-cloud-alibaba-dependencies-1.5.0.RELEASE 为例: <dependency> <groupId>com.alibaba. ...
- requests模块(请求接口)
下面分别是get,post,入参json,添加cookie,添加header,上传/下载文件 的接口请求举例: import requests #导入模块 #1.发get请求 url = 'htt ...
- QTP技术支持之QTP对象无法识别(转自582357212的个人空间,链接:http://www.51testing.com/html/64/305564-847787.html)
QTP自动化测试从业者,或者很多练习使用QTP开发自动化测试代码的人员遇到最多的问题恐怕就是对象无法识别了,对象无法识别原因有很多种,根据经常对QTP自动化测试脚本开发人员的技术Support,我总结 ...
- struts2 基础
框架(frameWork):某一种应用的半成品 struts2: 表现层 处理与页面进行交互的相关功能 hibernate: 持久层 负责业务逻辑数据的持久化 spring: 业务层 负责复杂的业 ...
- linux 截取变量字符串
STR=123456abc FINAL=`echo ${STR: -1}` 或者 FINAL=${STR: -1} 都可以让FINAL获得c这个最后一个字符 Linux 的字符串截取很有用.有八种 ...
- Java数据结构之队列(Queue)
1.使用场景 银行排队的案例: 2.队列介绍 队列是一个有序列表,可以用数组或是链表来实现. 遵循先入先出的原则: 先存入队列的数据,要先取出. 后存入的要后取出 示意图:(使用数组模拟队列示意图) ...
- SUSTOJ_路痴的单身小涵(图中最短路的条数)
去年因为太low没有做出来校赛的最后一题,遂今年校赛做了这个题,下面我做详细描述. 原题链接 本题大意:给定一个无向图G,每个边的权值为1,图中L表示起点,C表示终点,#表示未通路,给定时间k,让你判 ...
- 【洛谷p2239】螺旋矩阵
关于题前废话: 这道题的数据范围过于强大了qwq,显然如果我们开一个30000*30000的二维数组来模拟,显然首先就开不下这么大的数组,然后暴力搜索的话也会爆掉,所以直接模拟显然是一个不正确的选择( ...
- 关于Python学习的一点说明
关于Python学习的一点说明 我是用什么地方的资料来学习python的呢? 答案当然是鼎鼎大名的GitHub了. 5万多星推荐,100天让你从入门到精通,你值得拥有,点我进入查看
- NGUI的HUD Text的扩展插件学习--(UIFollowTarget)的使用
一,我们先导入NGUI_HUD_Text_v1.11包,导入包后会在项目生成一个这样的文件夹 二,我们添加一个cube,给cube添加一个空的游戏对象 二,我们使添加一个label,然后给label添 ...