Medium
题目描述
给你一个整数数组 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 * 10^41 <= nums[i] <= 10000 <= k <= 10^6
解题思路
解题思路
这道题要求找出所有乘积小于 k 的连续子数组的数量。我们可以使用滑动窗口的思想来解决这个问题。
核心思路:
- 使用双指针维护一个滑动窗口,窗口内所有元素的乘积小于 k
- 当乘积 >= k 时,收缩左边界直到乘积再次小于 k
- 对于每个右端点,计算以该点为右端点且乘积小于 k 的子数组个数
关键观察: 当我们固定右端点 right 时,以 right 为右端点的合法子数组数量等于 right - left + 1。这是因为左端点可以是 left, left+1, …, right 中的任意一个。
算法步骤:
- 初始化 left = 0, product = 1, count = 0
- 遍历数组,right 指针向右移动
- 将 nums[right] 乘入 product
- 当 product >= k 时,将 nums[left] 从 product 中除去,left 右移
- 累加当前窗口大小到结果中
时间复杂度优化: 由于每个元素最多被访问两次(一次加入窗口,一次移出窗口),所以时间复杂度为 O(n)。
注意事项: 当 k <= 1 时,由于所有元素都是正整数,不可能有乘积小于 k 的子数组,直接返回 0。
代码实现
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if (k <= 1) return 0;
int left = 0, count = 0;
int product = 1;
for (int right = 0; right < nums.size(); right++) {
product *= nums[right];
while (product >= k) {
product /= nums[left];
left++;
}
count += right - left + 1;
}
return count;
}
};
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1:
return 0
left = 0
count = 0
product = 1
for right in range(len(nums)):
product *= nums[right]
while product >= k:
product //= nums[left]
left += 1
count += right - left + 1
return count
public class Solution {
public int NumSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int left = 0, count = 0;
int product = 1;
for (int right = 0; right < nums.Length; right++) {
product *= nums[right];
while (product >= k) {
product /= nums[left];
left++;
}
count += right - left + 1;
}
return count;
}
}
var numSubarrayProductLessThanK = function(nums, k) {
if (k <= 1) return 0;
let left = 0, count = 0;
let product = 1;
for (let right = 0; right < nums.length; right++) {
product *= nums[right];
while (product >= k) {
product = Math.floor(product / nums[left]);
left++;
}
count += right - left + 1;
}
return count;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个元素最多被访问两次,一次被右指针访问,一次被左指针访问 |
| 空间复杂度 | O(1) | 只使用了常数级别的额外空间 |
相关题目
. Maximum Product Subarray (Medium)
. Maximum Size Subarray Sum Equals k (Medium)
. Subarray Sum Equals K (Medium)
. Two Sum Less Than K (Easy)