题目
题目描述:给定一个正整数数组 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的下标最小左边界,然后计算出该滑动窗口中所有的连续子数组数量。
计算完以上一个滑动窗口中所有的连续子数组数量后,将右边界右移一位,接着计算新的滑动窗口中所有的连续子数组数量,然后将所有的数量相加即为总数量。
这题的重点就是如何计算一个滑动窗口中所有的连续子数组数量。因为子数组元素需要符合连续的要求,所以子数组中必须包含的元素就是右边界位置上的元素,然后可以发现其子数组的数量实际上就等于。其中, 和 分别为窗口左、右边界的下标值。
Python3代码
- 时间复杂度:。其中是数组 nums 的长度。首先循环的时间复杂度为,而 最多也只会移动 次。
- 空间复杂度:。
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