越长越合法
核心思路:
当滑窗满足条件时进入循环缩小窗口并累加当前满足条件的子数组次数。
当循环结束后滑窗不满足条件,而head就刚好就是满足条件的子数组的次数。
如果当前位置不能进入循环也要加上之前记录的次数,因为越长越合法。
再次进入循环时,会对之前满足条件的子数组次数累加当前区间内满足条件子数组次数。
这样就能记录0(动态)~当前位置(不变)中满足条件的所有子数组次数。遍历一次数据就能获取所有合法子数组个数,记录结果一般要写 ans += left
。
模板:
for(int i=0;i<s.size();i++){
//记录条件
while(满足条件){ //进入循环记录当前区间满足条件次数
//删除数据
//更新窗口头位置
}
ret+=head; //只要前面有满足条件子数组,都能进行累加
}
力扣题单练习(灵神题单中摘取题目)
class Solution {
public:
int numberOfSubstrings(string s) {
int ret=0,head=0;
unordered_map<int,int> map;
for(int i=0;i<s.size();i++){
if(s[i]=='a' || s[i]=='b' || s[i]=='c') map[s[i]]++; //记录条件
while(map['a'] && map['b'] && map['c']){ //进入循环记录当前区间满足条件次数
map[s[head]]--;
head++;
}
ret+=head; //只要前面有满足条件子数组,都能进行累加
}
return ret;
}
};
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
int buff=0,head=0;
for(auto x:nums) buff=max(buff,x);
long long ret=0;
unordered_map<int,int> map;
for(int i=0;i<nums.size();i++){
if(nums[i]==buff) map[buff]++; //记录条件满足次数
while(map[buff]==k){
map[nums[head]]--; //删除数据
head++; //缩小窗口
}
ret+=head; //记录从0~当前位置所有满足条件的子数组
}
return ret;
}
};
class Solution {
public:
int numberOfSubstrings(string s, int k) {
unordered_map<char,int> map;
int ret=0,head=0;
for(int i=0;i<s.size();i++){
map[s[i]]++; //记录条件
while(map[s[i]]==k){ //当前满足条件
map[s[head]]--; //删除数据
head++; //缩小窗口
}
ret+=head; //记录结果
}
return ret;
}
};
class Solution {
public:
int countCompleteSubarrays(vector<int>& nums) {
unordered_map<int,int> map;
for(auto x:nums) map[x]++;
int k=map.size(); //总数据中不同元素的数量
int ret=0,head=0,buff=0;
map.clear();
for(int i=0;i<nums.size();i++){
map[nums[i]]++;
if(map[nums[i]]==1) buff++; //记录不同元素的数量
while(buff==k){ //满足条件
map[nums[head]]--; //删除数据
if(map[nums[head]]==0) buff--; //当滑窗内彻底删除了某个元素(当前元素数量为0),破坏满足条件
head++; //缩小窗口
}
ret+=head; //更新结果
}
return ret;
}
};
题意:好子数组定义:相同元素不同下标的组合数量>=k。统计好子数组的数量
思路:
统计组合数量(用哈希表记录不同元素的数量,这样就可以遍历相同元素的集合)
因为哈希表的缘故能遍历相同元素的集合,每次累加从头开始到当前位置元素数量-1即可。遍历到最后即是这个元素组合的总量。
相同元素集合每减少一个数量,就会失去size-1个组合
class Solution {
public:
long long countGood(vector<int>& nums, int k) {
//题意:好子数组定义:相同元素不同下标的组合数量>=k。统计好子数组的数量
//思路:统计组合数量(用哈希表记录不同元素的数量,这样就可以遍历相同元素的集合)
//因为哈希表的缘故能遍历相同元素的集合,每次累加从头开始到当前位置元素数量-1即可。遍历到最后即是这个元素组合的总量。
//相同元素集合每减少一个数量,就会失去size-1个组合
unordered_map<int,int> map;
long long ret=0;
int head=0,buff=0;
for(int i=0;i<nums.size();i++){
map[nums[i]]++;
buff+=map[nums[i]]-1; //记录条件
while(buff>=k){ //满足条件
map[nums[head]]--; //删除数据
buff-=map[nums[head]]; //减去对应元素的组合数量
head++; //缩小窗口
}
ret+=head; //更新答案
}
return ret;
}
};
3298. 统计重新排列后包含另一个字符串的子字符串数目 II
题意:统计word1中子字符串重新排列是以word2为前缀的数量
思路:
先用哈希表记录需要的条件元素数量。遍历word1满足条件就记录。
当窗口元素都大于或等于需要元素。进入循环累加区间满足条件数量。
如果当前元素不满足条件也要加上之前满足条件的数量,因为越长越合法
class Solution {
public:
//判断窗口元素是否都大于或等于需要元素
bool check(unordered_map<char,int>& need, unordered_map<char,int>& win){
for(auto [a,b]: need) if(win[a]<b) return false;
return true;
}
long long validSubstringCount(string word1, string word2) {
//题意:统计word1中子字符串重新排列是以word2为前缀的数量
//思路:先用哈希表记录需要的条件元素数量。遍历word1满足条件就记录。
//当窗口元素都大于或等于需要元素。进入循环累加区间满足条件数量。
//如果当前元素不满足条件也要加上之前满足条件的数量,因为越长越合法
long long ret=0;
int head=0;
unordered_map<char,int> need,win;
for(auto x : word2) need[x]++; //记录条件元素数量
for(auto x : word1){
if(need.find(x)!=need.end()) win[x]++;
while(check(need,win)){ //当窗口元素都满足需要元素时进入窗口
if(need.find(word1[head])!=need.end()) win[word1[head]]--; //删除数据
head++; //缩小窗口
}
ret+=head; //更新结果
}
return ret;
}
};