Day11--滑动窗口与双指针--713. 乘积小于 K 的子数组,3258. 统计满足 K 约束的子字符串数量 I,2302. 统计得分小于 K 的子数组数目

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值