算法刷题自记录 | Leetcode713. 乘积小于K的子数组(滑动窗口,窗口大小不固定)

题目

题目描述:给定一个正整数数组 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

思路

  本质是滑动窗口问题,但本题的滑动窗口大小是不固定的。首先需要理解题目中“连续的子数组”的意思是子数组中的元素在原数组 nums 中的位置是连续的,即不能出现诸如 [10, 2] 这样的子数组。

  对于滑动窗口来说,本题相当于固定滑动窗口的右边界,因为题目要求的是求乘积小于 k 的子数组,所以每次将窗口左边界右移一位时,滑动窗口中元素的总乘积只会小于等于右移前滑动窗口中元素的总乘积,即在固定右边界的情况下,滑动窗口中元素的总乘积是单调不增的。因此,只需要找出乘积小于k的下标最小左边界,然后计算出该滑动窗口中所有的连续子数组数量。

  计算完以上一个滑动窗口中所有的连续子数组数量后,将右边界右移一位,接着计算新的滑动窗口中所有的连续子数组数量,然后将所有的数量相加即为总数量。

  这题的重点就是如何计算一个滑动窗口中所有的连续子数组数量。因为子数组元素需要符合连续的要求,所以子数组中必须包含的元素就是右边界位置上的元素,然后可以发现其子数组的数量实际上就等于right-left+1。其中,right 和 left 分别为窗口左、右边界的下标值。

Python3代码

  • 时间复杂度:O(N)。其中N是数组 nums 的长度。首先循环的时间复杂度为O(N),而 left 最多也只会移动 N 次。
  • 空间复杂度:O(1)
class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        if k <= 1:
            return 0
        ans = 0
        left = 0
        prod = 1

        for right in range(len(nums)):
            prod *= nums[right]
            while prod >= k:            # if 当前滑动窗口所有元素的乘积 < k,则该窗口的所有连续的子数组各自的乘积都 < k,所以只需要统计该窗口的所有连续的子数组个数即可
                prod /= nums[left]
                left += 1               # 将窗口左边界右移一位,即窗口大小减1
            ans += right - left + 1     # 关键!!窗口大小[right - left + 1]的滑动窗口,其含有的连续的字数组个数即为right - left + 1
        return ans

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值