什么时候使用不定长滑动窗口(尺取法)?
寻找一个容器中是否有(有多少个)子容器符合某种条件,根据上述,这个条件大概率是指:小于某某值,最长不重复。可以使用尺取法。
尺取法做法
尺取法做法: 现在遇到的尺取法就是固定右边界,这时该窗口刚好符合不超过条件,当右边界往右再移动一下就会不符合条件。这时就需要右移左边界。左右指针都只会增加不会减小。
跟前缀和法的区别?
什么时候尺取法?什么时候前缀和法?那种场景下使用呢?
leetcode题
713. 乘积小于 K 的子数组
给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。
- 用什么方法?为啥用这个方法?
根据题目要求关键点在于 找到子数组小于k。子数组 && 小于某某值,考虑尺取法。当从头开始,右边界一直右移,窗口积当>=k时,若右边界还是右移,则积只会越来越大,所以此时固定右边界,右移左边界,直到积<k(符合条件)。当窗口积<k时,则窗口中的子数组积都是<k。
计算每个右边界对应符合条件的子数组数量:为什么是res=right-left+1而不是res=1+2+…+(right-left+1)?因为这是计算的只包含新添加进来的右边界元素的子数组,避免了与前面的重合。 例如第二次循环时 10*5<k 此时以及记录过[10] 该窗口子数组只需记录[5] [10,5]即可,也就是+2,而后者是+3,这就重复记录了[10]。
- 代码
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
int len = nums.size();
int res = 0;
int left = 0,right = 0;
int product = 1;
for(right;right<len;++right){
product *= nums[right];
while(left<=right && product>k){
product /= nums[left];
++left;
}
res += right-left+1;//就算知道这个方法也想不到的地方
}
return res;
}
};
- 学到了什么?
尺取法求窗口中的子数组的个数
尺取法的思路做法
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
- 用什么方法?为啥用这个方法?
看到题目知道是找子串,可以考虑滑动窗口。
- 右指针一直往前走(窗口增长),直到添加右指针指向的元素后不符合条件(本题是有重复)
- 当右指针走到不满足条件(本题是有重复)时候需要调整左值针(也就是缩短窗口),直到再次满足条件(子串中无重复字符)
- 本题使用hash表来记录字符是否有重复。
评论中看到的方法:
每次添加一个,查看新添加元素是否与之前的字符相同,并记录长度。当相同时就标记与新添加的元素相同元素前的位置flag,下次新添加进来的元素与之前元素进行比较时就比较到flag就行,不必一直比较到0。每次新添加进来 进行比较时,len初始都为一。
- 代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.size();
if(len == 0) return 0;
int res = 0;
unordered_map<char,int> hashmap;
int left = 0;
for(int right = 0;right<len;right++){
++hashmap[s[right]];
while(hashmap[s[right]]>1){
--hashmap[s[left]];
++left;
}
if(right-left+1>res) res = right-left+1;
}
return res;
}
};
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size() == 0) return 0;
int maxlen = 1,flag = 0;
for(int i = 1;i<s.size();++i){
int len = 1;
for(int j = i;j>flag;j--){
//这里非常重要,保证了flag指向与新添加字符相同字符前一个位置。
if(s[i]!=s[j-1]){
len++;
}else{
flag = j;
break;
}
}
maxlen = max(len,maxlen);
}
return maxlen;
}
};
- 学到了什么?
- 对于寻找不重复子容器的处理不仅仅可以用滑动窗口进行调整,还可以使用新添加一个元素,让该元素与现在的子容器中元素进行对比来判断是否有重复的元素。
697. 数组的度
- 用什么方法?为啥用这个方法?
我能想到用哈希表记录下数组中每个元素的度,然后找到最大度对应的元素,并且能想到最短子数组两端一定是该元素。但是没有想到怎么解决:因为符合条件的元素可能有多个,即多个不同的数在原数组中出现次数相同
看解答后可以一个元素在和哈希表中对应一个保存三个元素数组(出现次数,第一次出现的下标,最后一次出现的下标)这样就解决了我解决不了的。 - 代码
我自己写的 通过87/89
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
int len = nums.size();
int degree = 0;
int num;
int left = 0,right=len-1;
unordered_map<int,int> hashmap;
for(int i = 0;i<len;++i){
++hashmap[nums[i]];
if(hashmap[nums[i]]>degree){
degree = hashmap[nums[i]];
num = nums[i];
}
}
while(nums[left]!=num) left++;
while(nums[right]!=num) right--;
return right-left+1;
}
};
看别人的代码,可以不使用数组记录第一次出现和最后一次出现,就能判断出子数组的长度
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
int len = nums.size();
int degree = 0,res = 50000;
int hasmap[50000] = {0};
for(int i = 0;i<len;++i){//统计整个数组的度为多大,并记录每个元素出现的次数
++hasmap[nums[i]];
if(hasmap[nums[i]]>degree) degree = hasmap[nums[i]];
}
if(degree==1) return 1;
int current_degree = 0;
int current_maxlen = 0;
for(int i = 0;i<len;++i){
if(hasmap[nums[i]]==degree){//只处理出现次数与度相等的元素
//当current_degree减为1时,j就指向nums[i]最后一次出现的位置 这样就解决了我的算法里解决不了的问题,可以判断子数组的长度了。
current_degree = degree;
for(int j = i+1;j<len;++j){
if(nums[j]==nums[i]) current_degree--;
if(current_degree==1){
current_maxlen = j-i+1;
res = min(current_maxlen,res);
hasmap[nums[i]] = 0;//该元素以及判断完成,需要清除,避免重复判断。
break;
}
}
}
}
return res;
}
};
官方题解代码
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
int len = nums.size();
int degree = 0,minlen = 50000;
unordered_map<int,vector<int>> hashmap;
for(int i = 0;i<len;++i){
if(hashmap.count(nums[i])){
hashmap[nums[i]][0]++;
hashmap[nums[i]][2] = i;
}else{
hashmap[nums[i]] = {1,i,i};
}
}
for(auto [_,vec]:hashmap){
if(vec[0]>degree){
degree = vec[0];
minlen = vec[2]-vec[1]+1;
}else if(vec[0]==degree){
if(vec[2]-vec[1]+1<minlen) minlen = vec[2]-vec[1]+1;
}
}
//对哈希表存储和遍历 重要C++知识点
//for(auto [_,vec]:hashmap){
// if(vec[0]>=degree){
// degree = vec[0];
// if(vec[2]-vec[1]+1<minlen){
// minlen = vec[2]-vec[1]+1;
// }
// }
//}
return minlen;
}
};
- 学到了什么?
- 哈希表一个元素对应一个数组
- 哈希表存储顺序