[滑动窗口] 剑指 Offer II 009. 乘积小于 K 的子数组

202 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

每日刷题 2022.08.22

题目

  • 给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。

示例

  • 示例1
输入: 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的子数组。
  • 示例2
输入: nums = [1,2,3], k = 0
输出: 0

提示

  • 1 <= nums.length <= 3 * 104
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 106

解题思路

  • 使用滑动窗口的容斥原理。双指针实现的滑动窗口,右指针一直往后,直到区间内包含的元素不符合要求,就移动左区间的指针,缩小区间,直到区间包含的元素再次符合要求时,停止左区间的移动,再移动右区间的指针。
  • 初始化 l 和 r 两个指针,同时从 0 开始
  • r 向右遍历,直到 r = nums.length - 1
  • 如果 r 小于 k, 表示 r 当前这个数单独满足要求,答案 + 1
  • 此时 while 循环将 l 左移,每次判断是否满足乘积小于 k,满足答案 + 1
  • 直到乘积大于等于 k,无法进入 while 循环
  • 收回 l, 与 r 同步右移,重复上述步骤

AC代码

var numSubarrayProductLessThanK = function(nums, k) {
  // 使用滑动窗口,每次新添加进来的长度,新增加的个数,就是当前的下标差值
  let n = nums.length, ans = 0;
  if(k <= 1) return 0;
  for(let i = 0, j = 0, multi = 1; i < n; i++) {
    multi *= nums[i];
    console.log(multi)
    while(multi >= k) {
      multi /= nums[j++];
    }
    // 当前的数值的长度, 总的都会加上的
    ans += i - j + 1;
  }
  return ans;
};