给你一个整数数组
nums和一个 正整数k。请你统计有多少满足 「
nums中的 最大 元素」至少出现k次的子数组,并返回满足这一条件的子数组的数目。子数组是数组中的一个连续元素序列。
示例 1:
输入:nums = [1,3,2,3,3], k = 2 输出:6 解释:包含元素 3 至少 2 次的子数组为:[1,3,2,3]、[1,3,2,3,3]、[3,2,3]、[3,2,3,3]、[2,3,3] 和 [3,3] 。示例 2:
输入:nums = [1,4,2,1], k = 3 输出:0 解释:没有子数组包含元素 4 至少 3 次。提示:
1 <= nums.length <= 1051 <= nums[i] <= 1061 <= k <= 105
首先使用N^2的方法,固定左端点,枚举所有的子数组,然后看他们中最大值出现的次数如果大于k就++
优化:发现当cnt为k的时候,后续就不需要处理了,直接把j后面所有的值用n-j+1就能加入结果中
优化:再次优化,发现之前的j不需要每次都从当前i这个位置进行后续判断,可以采用滑动窗口的思想去处理这个题目,j不需要回退
之前写的题都是控制右窗口,这次使用控制左窗口,固定左边界,
cpp
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
long long res = 0;
int mx = *max_element(nums.begin(), nums.end()); // 获取数组中的最大值
int cnt = 0, n = nums.size();
for(int i = 0, j = 0; i < n; i++) // 控制左端点
{
while(j < n && cnt < k) // 进窗口
{ // 让j去往右访问,当j到达边界或者最大值出现了k次时停下
if(nums[j] == mx)
cnt++;
j++;
}
if(cnt == k)
res += n-j+1; // 维护结果 j之后的所有数组都满足条件
if(nums[i] == mx) // 出窗口 左端点进行出窗口
cnt--;
}
return res;
}
};
采用固定右边界的方式:不需要一些多余的判断
cpp
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
// unordered_map<int,int> window;
int left = 0, right = 0, n = nums.size();
int cnt = 0, mx = *max_element(nums.begin(), nums.end());
long long res = 0;
while(right < n) // 这次采用固定右端点,维护所有左边界
{
if(nums[right] == mx) // 入窗口
cnt++;
while(cnt >= k) // left到right内部都是符合条件的
{ // 所以让left到达一个临界点
if(nums[left] == mx)
cnt--;
left++;
}
res += left; // 保存left之前所有符合条件的结果
right++;
}
return res;
}
};
由于子数组越长,包含的元素越多,越能满足题目要求;反之,子数组越短,包含的元素越少,越不能满足题目要求。有这种性质的题目,可以用滑动窗口解决。