乘积小于 K 的子数组

73 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

示例

输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10][5][2],、[6][10,5][5,2][2,6][5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
输入: nums = [1,2,3], k = 0
输出: 0

提示

  • 1 <= nums.length <= 3 * 10^4
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 10^6

滑动窗口

这道题要求的是连续的子数组且乘积严格小于目标值k,那么我们可以使用滑动窗口的方式,不断的累积乘积,直至其超过目标值,再移动左边界,重复前面的步骤。长度的计算可以通过右边界减去左边界来求得区间长度。

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        // 边界判断
        if(k == 0){
            return k;
        }
        
        // 初始化
        int n = nums.length, res = 0;
        int tmp = 1;
        
        // 双指针滑动窗口
        for(int i = 0, j = 0; j < n; ++j){
            // 区间求积
            tmp *= nums[j];
            // 当结果大于k,则左边界右移
            while(i <= j && tmp >= k){
                tmp /= nums[i++];
            }
            // 累加区间长度
            res += j - i + 1;
        }
        // 返回结果
        return res;
    }
}

滑动窗口模版

public void method(int[] arr){
    // 边界指针
    int left = 0, right = 0;
    int res = 0, len = 0;
    
    while(right < arr.length){
        // 先移动右边的指针,累加元素之类的操作
        res += arr[right++];
        
        // 判断是否超过条件
        while(left < right && res < targer){
            // 超过的话则左边界移动
            res -= arr[left++];
        }
        
        // 区间长度
        len += right - left + 1;
    }
    
}

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组 nums\textit{nums} 的长度。两个端点 ij的增加次数都不超过 n

  • 空间复杂度:O(1)