Day61 | 灵神 | 滑动窗口:统计最大元素出现至少K次的子数组
2962.统计最大元素出现至少K次的子数组
2962. 统计最大元素出现至少 K 次的子数组 - 力扣(LeetCode)
思路:
一开始我并没有想出来,这是笔者的代码,很遗憾超时了,因为这个和暴力的思路差不多
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
long long l=0,res=0;
int max_num = ranges::max(nums),max_cnt=0;
for(int i=0;i<nums.size();i++)
{
if(max_num==nums[i])
max_cnt++;
int temp=max_cnt;
while(max_cnt>=k)
{
res++;
if(nums[l]==max_num)
max_cnt--;
l++;
}
l=0;
max_cnt=temp;
}
return res;
}
};
核心思路还是和前面滑动窗口的题一样,都是固定并遍历右端点,然后去寻找左指针收缩的条件。
本题左指针收缩的条件就是当前左右端点区间内的最大值的数量max_cnt大于等于k时,左指针收缩,收缩到当前左右端点区间内的最大值数量max_cnt小于k的时候就可以停止了。
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
long long l=0,res=0;
int max_num = ranges::max(nums),max_cnt=0;
for(int i=0;i<nums.size();i++)
{
if(max_num==nums[i])
max_cnt++;
int temp=max_cnt;
while(max_cnt>=k)
{
res++;
if(nums[l]==max_num)
max_cnt--;
l++;
}
}
return res;
}
};
一开始并没有下面这两行
l=0;
max_cnt=temp;
后来加上是因为我发现了我的代码的漏洞
比如区间为[1,3,2,3]时,我固定了第二个3,当左指针收缩到第一个3的时候会把max_cnt–,这就导致当区间为[1,3,2,3,3]时,我的max_cnt=2,而不是3。
所以我加上了上面这两行,让右边固定的时候左边从头开始计算。可是这样就和暴力解法的时间复杂度差不多了
问题的核心在于如何统计子数组的数量
如果在max_cnt–之后,仍然不影响统计满足条件的子数组的数量呢?
答案是在循环退出后直接 res+=l
举两个例子
当右端点固定为第二个3,区间为[1,3,2,3],这时左指针往左移动,移动到l=2,即区间为[2,3]时,max_cnt变为1,退出了循环,而满足条件的子数组为[1,3,2,3],[3,2,3],而l正好就是2,因为我们左指针移动过的路径就是所有满足条件的子数组。
再举一个例子:
右端点固定为第3个3,区间为[1,3,2,3,3],这是我们的l从2开始,真实区间其实是[2,3,3],这其实没有关系,因为l的2在这里其实代表的是[1,3,2,3,3]和[3,2,3,3]这两个子数组。然后移动到最后一个数[3],此时l=4,这四个子数组就是[1,3,2,3,3]、[3,2,3,3]、[2,3,3]、[3,3],没错,就是左指针走过的路径长度,即l的大小,所以直接res+=l就记录了子数组的个数.
完整代码:
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
long long l=0,res=0;
int max_num = ranges::max(nums),max_cnt=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]==max_num)
max_cnt++;
while(max_cnt==k)
{
if(nums[l]==max_num)
max_cnt--;
l++;
}
res+=l;
}
return res;
}
};