力扣 leetcode 713. 乘积小于 K 的子数组
问题描述
给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。
提示:
1 <= nums.length <= 3 * 104
1 <= nums[i] <= 1000
0 <= k <= 106
示例
示例 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
解题思路
本题要计算乘积小于 k 的子数组个数,可以维持一个滑动窗口,利用滑动窗口计算子数组的个数。
首先,我们要找出子数组个数的规律。假设我们当前滑动窗口内有 n 个元素,新添加一个元素 x 后,子数组的个数增加 y 个,那么,如何计算这个 y 呢?具体分析如下:
- x 可以单独组成一个子数组,y += 1
- x 与 n - 1, n - 2, n - 3, ... , 0 组合成 [x, n - 1], [x, n - 1, n - 2], [x, n - 1, n - 2, n - 3], ... , [x, n - 1, n - 2, n - 3, ... , 0] 的子数组,这些子数组共有 n 个
因此,添加 x 后,y = n + 1。那么,如果我们移动滑动窗口,是否会造成重复计算呢?答案是否定的。我们移动滑动窗口后,再次统计子数组个数时,还是要将新的元素加入滑动窗口后才进行统计,而 y 表示的是一个新的元素加入后,子数组的新增个数,因此不会造成重复计算。
按照上述思路,我们实现以下代码:
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
int windowSize = 0;
int cnt = 0;
int sum = 1;
for(int i = 0; i < nums.size(); i++){
if(nums[i] >= k){ // 这里实际上只把开头的大于 k 的元素跳过
continue;
}
for(int j = i + windowSize; j < nums.size(); j++){
if(nums[j] < k){ // 判断 nums[j]是否大于k
sum *= nums[j];
windowSize++; // 将nums[j]加入滑动窗口
if(sum >= k){ // 判断当前滑动窗口内元素乘积是否大于k
int n = i;
for(; n < j; n++){ // 大于k 时,将滑动窗口内元素从前往后依次弹出,直到乘积小于k
sum /= nums[n];
windowSize--;
if(sum < k){
break;
}
}
i = n + 1;
}
cnt += windowSize; // 统计新增子数组个数
}
else{ // 发现大于k的元素,跳出循环,清空滑动窗口
sum = 1;
i = j;
windowSize = 0;
break;
}
}
}
return cnt;
}
};
本文来自博客园,作者:greatestchen},转载请注明原文链接:https://www.cnblogs.com/greatestchen/p/16948374.html