Day11–滑动窗口与双指针–713. 乘积小于 K 的子数组,3258. 统计满足 K 约束的子字符串数量 I,2302. 统计得分小于 K 的子数组数目
今天要训练的题目类型是:【不定长滑动窗口】,题单来自@灵艾山茶府。
滑动窗口相当于在维护一个队列。右指针的移动可以视作入队,左指针的移动可以视作出队。
不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,求子数组个数。
今天的题目类型是:求子数组个数——求最长(越短越合法)
713. 乘积小于 K 的子数组
思路【@灵艾山茶府】:
由于子数组越长,乘积越大,越不能满足题目要求;反之,子数组越短,乘积越小,越能满足题目要求。有这种性质的题目,可以用滑动窗口解决。
枚举子数组右端点
right
,如果发现子数组不满足要求,就缩小窗口,也就是增大左端点left
。内层循环结束后,
[left,right]
这个子数组是满足题目要求的。由于子数组越短,越能满足题目要求,所以除了[left,right]
,还有[left+1,right],[left+2,right],…,[right,right]
都是满足要求的。也就是说,当右端点固定在right
时,左端点在left,left+1,left+2,…,right
的所有子数组都是满足要求的,这一共有right−left+1
个,加到答案中。
往后做了几道题之后,发现这道题简直就是模板题。
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) {
return 0;
}
int n = nums.length;
long prod = 1;
int left = 0;
int count = 0;
for (int i = 0; i < n; i++) {
// 1,入
prod *= nums[i];
// 2,出
while (prod >= k) {
prod /= nums[left];
left++;
}
// 3,更新(求个数:窗口的大小就是合格的数量的大小)
// 因为`[left+1,right],[left+2,right],…,[right,right]`都是满足要求的
count += i - left + 1;
}
return count;
}
}
3258. 统计满足 K 约束的子字符串数量 I
思路【我】:
思路同上题。
class Solution {
public int countKConstraintSubstrings(String s, int k) {
char[] ch = s.toCharArray();
int n = ch.length;
int left = 0;
int zero = 0;
int one = 0;
int count = 0;
for (int i = 0; i < n; i++) {
// 1,入
if (ch[i] == '0') {
zero++;
} else {
one++;
}
// 2,出
while (zero > k && one > k) {
if (ch[left] == '0') {
zero--;
} else {
one--;
}
left++;
}
// 3,更新(求个数:窗口的大小就是合格的数量的大小)
// 因为`[left+1,right],[left+2,right],…,[right,right]`都是满足要求的
count += i - left + 1;
}
return count;
}
}
2302. 统计得分小于 K 的子数组数目
思路【我】:
注意这里返回的类型是long。prod,sum,count都要定义为long类型。
其余思路同上题。
class Solution {
public long countSubarrays(int[] nums, long k) {
int n = nums.length;
long prod = 1;
long sum = 0;
int left = 0;
long count = 0;
int len = 0;
for (int i = 0; i < n; i++) {
// 1,入
sum += nums[i];
len = i - left + 1;
prod = sum * len;
// 2,出
while (prod >= k) {
sum -= nums[left++];
len--;
prod = sum * len;
}
// 3,更新(求个数:窗口的大小就是合格的数量的大小)
// 因为`[left+1,right],[left+2,right],…,[right,right]`都是满足要求的
count += i - left + 1;
}
// 注意这里返回的类型是long。prod,sum,count都要定义为long类型
return count;
}
}
不得不感慨@灵艾山茶府的题单的质量,连续几题都是用同一个方法,几乎不用修改就能得到答案。——反过来,我们自己做的时候,要锻炼自己,遇到这种题目,能够识别出来用什么方法做,这才是更重要的,不然就白练了。
思路:
这里len和prod这两个变量可以省去。
class Solution {
public long countSubarrays(int[] nums, long k) {
int n = nums.length;
long sum = 0;
int left = 0;
long count = 0;
for (int i = 0; i < n; i++) {
sum += nums[i];
while (sum * (i - left + 1) >= k) {
sum -= nums[left];
left++;
}
count += i - left + 1;
}
return count;
}
}