Given an array of positive integers nums
and an integer k
, your task is to find the number of contiguous subarrays where the product of all the elements in the subarray is less than k
.
0
.nums
are positive integers.1 <= nums.length <= 3 * 10^4
, 1 <= nums[i] <= 1000
, 0 <= k <= 10^6
.
At first glance, you might think to check every possible contiguous subarray, calculate its product, and count it if the product is less than k
. This brute-force approach is simple to understand but not efficient, especially for large arrays. For each starting index, we would try every ending index, multiply the elements, and check if the product stays below k
.
However, this approach is slow because it checks every subarray individually, leading to a time complexity of O(n2) or worse. To optimize, we need a way to avoid recalculating products from scratch for overlapping subarrays. Since all elements are positive, we can use a sliding window technique, expanding and shrinking a window over the array to efficiently keep track of the product.
The key insight is: if the product of elements from left
to right
is less than k
, then all subarrays ending at right
and starting from any index between left
and right
are also valid.
To solve the problem efficiently, we'll use the sliding window (two-pointer) technique:
left
and right
, both at 0, and set prod = 1
(the running product).right
from 0 to the end of the array:prod
by nums[right]
.prod >= k
and left <= right
, divide prod
by nums[left]
and increment left
. This shrinks the window until the product is less than k
.right
is right - left + 1
.right
position to get the total number of valid subarrays.This method works efficiently because it avoids redundant calculations and leverages the properties of positive numbers (products only increase as you add elements, so shrinking the window is safe).
Let's use nums = [10, 5, 2, 6] and k = 100 as an example.
left = 0
, prod = 1
, count = 0
Total = 1 + 2 + 2 + 3 = 8 valid subarrays.
The optimized approach is much faster for large arrays.
class Solution:
def numSubarrayProductLessThanK(self, nums, k):
if k <= 1:
return 0
prod = 1
result = 0
left = 0
for right, val in enumerate(nums):
prod *= val
while prod >= k:
prod //= nums[left]
left += 1
result += right - left + 1
return result
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if (k <= 1) return 0;
int prod = 1, res = 0, left = 0;
for (int right = 0; right < nums.size(); ++right) {
prod *= nums[right];
while (prod >= k) {
prod /= nums[left++];
}
res += right - left + 1;
}
return res;
}
};
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int prod = 1, res = 0, left = 0;
for (int right = 0; right < nums.length; ++right) {
prod *= nums[right];
while (prod >= k) {
prod /= nums[left++];
}
res += right - left + 1;
}
return res;
}
}
var numSubarrayProductLessThanK = function(nums, k) {
if (k <= 1) return 0;
let prod = 1, res = 0, left = 0;
for (let right = 0; right < nums.length; ++right) {
prod *= nums[right];
while (prod >= k) {
prod /= nums[left++];
}
res += right - left + 1;
}
return res;
};
This problem demonstrates the power of the sliding window technique for efficiently counting subarrays under a product constraint. By exploiting the property that all numbers are positive, we avoid redundant calculations and achieve linear time. The key insight is that, for each window, all subarrays ending at the current index and starting at any valid left boundary are automatically valid, and we can count them in constant time. This makes the solution both elegant and practical for large inputs.