LeetCode 2906 统计最大元素出现至少K次的子数组(滑动窗口)
给出一个示例:
输入: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] 。
该题也是一个比较简单的滑动窗口的题目,但是奈何我把题目看错了,导致一直想不到好的解决的方法,我把该题的【nums中的最大元素】省略掉了,看成了该数组中存在子数组的任意一个数只要有数量大于等于k的子数组有多少个。
所以理所应到的想到了用前缀和加滑动窗口,构造一个二维前缀和去统计每个值的出现次数,再通过遍历后的右边界减前边界得到,该区间的所有值的个数,如果有大于等于k的就记一次
class Solution {public long countSubarrays(int[] nums, int k) {int n = nums.length;int mx = Integer.MIN_VALUE;for(int num:nums){mx = Math.max(mx,num);}int[][] ans = new int[n+1][mx+1];for(int i=1;i<n+1;i++){ans[i][nums[i-1]]++;}int number = 0;for(int i=2;i<n+1;i++){int left = i-k;int right = i;for(int j=1;j<=mx;j++){if(ans[right][j]-ans[left][j]>=k) number++;}}return number;}
}
但代码本身还是存在问题且复杂度过高,很可能通过不了,爆超时等等。反应过来后,再重新理清了一遍思路和题目,才发现就是一个简单的滑动窗口,还是那句话,外循环右扩展,内循环左收缩。
class Solution {public long countSubarrays(int[] nums, int k) {int max = Integer.MIN_VALUE;for (int num : nums) {max = Math.max(max, num);}long res = 0;int count = 0;int left = 0;for (int right = 0; right < nums.length; right++) {if (nums[right] == max) {count++;}while (count >= k) {// 当前窗口满足条件,从 right 到数组末尾的所有子数组都满足res += nums.length - right;if (nums[left] == max) {count--;}left++;}}return res;}
}
错误代码就不解释了,解释一下我的正确代码。
先通过遍历找到最大值mx
然后开始滑动窗口,拿[1,3,2,3,3] k=2
举例吧
先遍历右边界,使得mx
值的数量大于等于k 得到对应的r=3
坐标,当前子数组记作[1,3,2,3]
和[1,3,2,3,3]
然后通过内循环遍历左边界,每遍历一次,只要还能进入内循环就说明子数组符合要求记res+=nums.length - right
那为什么是nums.length - right
呢,其实就是上面记作的子数组的数目,当left++后去除了子数组的1,子数组成为了[3,2,3]
和[3,2,3,3]
。