Subarray Product Less Than K
Description
You get an array of positive integers nums and an integer k.
Return the number of contiguous subarrays whose product is strictly less than k.
Examples
Example 1
Input: nums = [10, 5, 2, 6], k = 100
Output: 8
Explanation: The 8 subarrays are [10], [5], [2], [6], [10, 5], [5, 2], [2, 6] and [5, 2, 6]. [10, 5, 2] isn't one: its product is 100, not less than 100.
Example 2
Input: nums = [1, 2, 3], k = 0
Output: 0
Explanation: Every product is at least 1, so none is less than 0.
Constraints
- 1 ≤ nums.length ≤ 3 × 10^4
- 1 ≤ nums[i] ≤ 1000
- 0 ≤ k ≤ 10^6
Editorial
Brute Force
Intuition
Try every start i. Multiply the numbers from i to the right one at a time, and count each subarray while its product is below k.
Every number is at least 1, so a product never shrinks as the subarray grows. The first time it reaches k, every longer subarray from i reaches it too, so you can stop and try the next start.
Step-by-Step Explanation
Let's try every start in nums = [10, 5, 2, 6], k = 100:
- Start at 0 and multiply to the right. 2 subarrays from 0 stay below 100. The next product reaches 100, so stop, because longer ones from 0 only get bigger.
- Start at 1 and multiply to the right. 3 subarrays from 1 stay below 100.
- Start at 2 and multiply to the right. 2 subarrays from 2 stay below 100.
- Start at 3 and multiply to the right. 1 subarray from 3 stays below 100.
That's 8 subarrays.
Multiply Out From Every Start — Each step starts at one index and counts the subarrays from there whose product stays below k (green).
Algorithm
- Set
count = 0. - For each start
i:- set
product = 1; - for each
jfromi: multiplyproductbynums[j]; if it's at leastk, stop; otherwise add 1 tocount.
- set
- Return
count.
Code
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
int count = 0;
for (int i = 0; i < (int)nums.size(); i++) {
long long product = 1;
for (int j = i; j < (int)nums.size(); j++) {
product *= nums[j];
if (product >= k) break; // longer subarrays from i only get bigger
count++;
}
}
return count;
}
};
class Solution:
def numSubarrayProductLessThanK(self, nums: list[int], k: int) -> int:
count = 0
for i in range(len(nums)):
product = 1
for j in range(i, len(nums)):
product *= nums[j]
if product >= k:
break # longer subarrays from i only get bigger
count += 1
return count
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int count = 0;
for (int i = 0; i < nums.length; i++) {
long product = 1;
for (int j = i; j < nums.length; j++) {
product *= nums[j];
if (product >= k) break; // longer subarrays from i only get bigger
count++;
}
}
return count;
}
}
var numSubarrayProductLessThanK = function(nums, k) {
let count = 0;
for (let i = 0; i < nums.length; i++) {
let product = 1;
for (let j = i; j < nums.length; j++) {
product *= nums[j];
if (product >= k) break; // longer subarrays from i only get bigger
count++;
}
}
return count;
};
Complexity Analysis
Time Complexity: O(n²)
When the products stay small (all 1s, say), every start multiplies all the way to the end.
Space Complexity: O(1)
Why This Approach Is Not Efficient
With 3 × 10^4 numbers that are all 1 and k = 10^6, no product ever reaches k, so the inner loop never stops early: 450,015,000 multiplications.
And it redoes work. The start i + 1 multiplies out almost the same numbers as the start i. Turn it around and count by where each subarray ends instead: for each right end, the subarrays with a small product all start at or after some smallest start, and a sliding window can track that start as the right end moves.
Optimal Approach - Sliding Window
Intuition
Keep a window [left, right] whose product is below k. Move right one step and multiply its number in. While the product is k or more, divide out nums[left] and move left on.
Now every subarray that ends at right and starts at left or later sits inside the window, so its product is below k too, and every one that starts before left isn't. That's exactly right - left + 1 subarrays, so add that many. Each subarray has one right end, so each is counted once.
One case first: every number is at least 1, so no product is ever below 1. When k ≤ 1 the answer is 0, and returning early also keeps the shrink from running past right.
Step-by-Step Explanation
Let's slide over nums = [10, 5, 2, 6], k = 100:
- The product is 10, below 100. Every window ending at 0 and starting at 0 or later counts, so add 1, which makes 1.
- The product is 50, below 100. Every window ending at 1 and starting at 0 or later counts, so add 2, which makes 3.
- The product is 100, not below 100, so shrink and 10 leaves.
- The product is 10, below 100. Every window ending at 2 and starting at 1 or later counts, so add 2, which makes 5.
- The product is 60, below 100. Every window ending at 3 and starting at 1 or later counts, so add 3, which makes 8.
The answer is 8.
Count the Windows Ending at Each Right — The window is shaded. After any shrink (the amber number leaves), every subarray ending at right inside the window counts (green).
Algorithm
- If
k ≤ 1, return 0. - Set
product = 1,left = 0,count = 0. - For each
right:- multiply
productbynums[right]; - while
product ≥ k: divide it bynums[left]and add 1 toleft; - add
right - left + 1tocount.
- multiply
- Return
count.
Code
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if (k <= 1) return 0; // every number is at least 1, so no product is below 1
long long product = 1;
int left = 0, count = 0;
for (int right = 0; right < (int)nums.size(); right++) {
product *= nums[right]; // grow
while (product >= k) { // shrink until the product is below k
product /= nums[left];
left++;
}
count += right - left + 1; // every window ending at right, starting at left or later
}
return count;
}
};
class Solution:
def numSubarrayProductLessThanK(self, nums: list[int], k: int) -> int:
if k <= 1:
return 0 # every number is at least 1, so no product is below 1
product = 1
left = count = 0
for right, x in enumerate(nums):
product *= x # grow
while product >= k: # shrink until the product is below k
product //= nums[left]
left += 1
count += right - left + 1 # every window ending at right, starting at left or later
return count
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0; // every number is at least 1, so no product is below 1
long product = 1;
int left = 0, count = 0;
for (int right = 0; right < nums.length; right++) {
product *= nums[right]; // grow
while (product >= k) { // shrink until the product is below k
product /= nums[left];
left++;
}
count += right - left + 1; // every window ending at right, starting at left or later
}
return count;
}
}
var numSubarrayProductLessThanK = function(nums, k) {
if (k <= 1) return 0; // every number is at least 1, so no product is below 1
let product = 1, left = 0, count = 0;
for (let right = 0; right < nums.length; right++) {
product *= nums[right]; // grow
while (product >= k) { // shrink until the product is below k
product /= nums[left];
left++;
}
count += right - left + 1; // every window ending at right, starting at left or later
}
return count;
};
Complexity Analysis
Time Complexity: O(n)
right moves n times and left at most n times, and neither moves back.
Space Complexity: O(1)
The product is always below k × 1000 = 10^9, so it fits in a 64-bit number (the code uses one anyway).
Common Mistakes
Each of these looks right on a quick read, and the tests still reject it. The outputs below come from running the code.
Adding right - left. count += right - left
On nums = [10, 5, 2, 6], k = 100 it returns 4; the answer is 8. The windows that end at right start at left, left + 1, …, right: that's right − left + 1 of them. right - left forgets the one that is just nums[right] on its own.
No guard for k ≤ 1. while product >= k: # with k = 0 this never stops
On nums = [1, 2, 3], k = 0 it crashes with IndexError: list index out of range; the answer is 0. Every number is at least 1, so no product is ever below 1. Without returning 0 first, the shrink never gets the product under k, and left runs off the end of the array.
Shrinking only once per step. if product >= k:
On nums = [2, 2, 2, 100], k = 50 it returns 9; the answer is 6. One large number can push the product far past k, and dropping a single number from the left may not be enough. Until the product is below k, the windows ending at right don't count: shrink with while.
Where This Pattern Is Taught
This problem practises counting subarrays with a sliding window: each step adds the right − left + 1 windows that end at right. It's taught in Counting Subarrays with a Sliding Window.