Subarray Product Less Than K: Sliding Window on Products
Subarray Product Less Than K is LeetCode #713, and it is a great problem for building your intuition around the sliding window pattern. You are given an array of positive integers and a target k, and you need to count how many contiguous subarrays have a product strictly less than k. The trick is that you do not need to enumerate every subarray. A two-pointer sliding window gets the answer in a single pass.
The problem
Given an array of positive integers nums and an integer k, return the number of contiguous subarrays where the product of all elements in the subarray is strictly less than k.
Input: nums = [10, 5, 2, 6], k = 100
Output: 8
The 8 subarrays with product less than 100 are:
[10], [5], [2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]
Input: nums = [1, 2, 3], k = 0
Output: 0
No subarray has a product less than 0.
The approach
The brute force way is to check every possible subarray, compute its product, and count the ones below k. That is O(n^2) work, and for arrays with up to 30,000 elements it will be too slow.
Instead, you can use a sliding window. Maintain two pointers, left and right, that define the current window. As you move right forward, multiply the running product by nums[right]. If the product becomes >= k, shrink the window from the left by dividing out nums[left] and advancing left. Once the window is valid again (product < k), every subarray ending at right and starting anywhere from left to right is also valid.
The number of such subarrays is right - left + 1. Why? Because the valid subarrays ending at index right are: [nums[right]], [nums[right-1], nums[right]], all the way to [nums[left], ..., nums[right]]. That is exactly right - left + 1 subarrays.
Each time right moves forward by one position, you add exactly right - left + 1 new valid subarrays. These are all the subarrays that end at the new right position. This avoids double-counting because subarrays ending at earlier positions were already counted in previous iterations.
The solution
def numSubarrayProductLessThanK(nums, k):
if k <= 1:
return 0
product = 1
count = 0
left = 0
for right in range(len(nums)):
product *= nums[right]
while product >= k:
product //= nums[left]
left += 1
count += right - left + 1
return count
Here is what each part does:
-
Early return for
k <= 1. Since all elements are positive integers (at least 1), no subarray can have a product less than 1. And a product less than 0 is impossible. So ifkis 1 or smaller, the answer is always 0. -
Initialize
product = 1andleft = 0. The running product starts at 1 (the multiplicative identity), and the window starts withleftat the beginning of the array. -
Expand by moving
right. For each newright, multiply the current element into the running product. -
Shrink while invalid. If the product is
>=k, divide out the leftmost element and advanceleft. Keep doing this until the product drops belowkor the window is empty. -
Count valid subarrays. After the window is valid,
right - left + 1gives the number of new valid subarrays ending atright. Add this to the total count.
Step-by-step walkthrough
Step 1: right = 0. Include nums[0] = 10. Product = 10. 10 < 100, so the window is valid.
Window [10]. Valid subarrays ending here: [10]. That is right - left + 1 = 1. New subarrays added: 1. Total so far: 1.
Step 2: right = 1. Include nums[1] = 5. Product = 50. 50 < 100, window is valid.
Window [10, 5]. Valid subarrays ending here: [5], [10, 5]. That is right - left + 1 = 2. New subarrays added: 2. Total so far: 3.
Step 3: right = 2. Include nums[2] = 2. Product = 100. 100 >= 100, so shrink: divide out nums[0] = 10, left moves to 1. Product = 10.
Window [5, 2]. After shrinking, product = 10. Valid subarrays ending here: [2], [5, 2]. That is right - left + 1 = 2. New subarrays added: 2. Total so far: 5.
Step 4: right = 3. Include nums[3] = 6. Product = 60. 60 < 100, window is valid.
Window [5, 2, 6]. Valid subarrays ending here: [6], [2, 6], [5, 2, 6]. That is right - left + 1 = 3. New subarrays added: 3. Total so far: 8.
After all four steps, the total count is 1 + 2 + 2 + 3 = 8. That matches the expected output. Notice how the left pointer only moved once (in step 3) when the product hit 100. The rest of the time, the window just kept expanding.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Brute force (all subarrays) | O(n^2) | O(1) |
| Sliding window | O(n) | O(1) |
Time: O(n). Each element is visited at most twice: once when right moves past it, and at most once when left moves past it. The inner while loop does not make this O(n^2) because left only moves forward and never resets. Across the entire run, left moves at most n times total.
Space: O(1). You only store a few variables: product, count, left, and right. No extra arrays or hash maps needed.
The building blocks
Sliding window pattern
This problem uses the "shrinking window" variant of sliding window. You grow the window by advancing right, and you shrink it by advancing left whenever the window condition is violated (product >= k). The goal is to count all valid windows, not just find the longest or shortest one.
The same sliding window skeleton shows up in many problems:
- Minimum Size Subarray Sum uses it to find the shortest subarray with a sum at least
k. - Longest Substring Without Repeating Characters uses it to find the longest window with unique characters.
- Longest Repeating Character Replacement uses it to find the longest window where you can make all characters the same with at most
kreplacements.
Use sliding window when the problem asks about contiguous subarrays (or substrings) and the constraint is monotonic: if a window is too big or violates the condition, making it bigger will not fix it. If shrinking might fix it, sliding window is likely the right approach.
Edge cases
k <= 1: Since all elements are positive integers, no product can be less than 1. Return 0 immediately. This also handlesk = 0and negative values ofk.- Single element: If the array has one element and it is less than
k, the answer is 1. Otherwise 0. The algorithm handles this naturally. - All elements equal to 1: Every subarray has a product of 1. If
k > 1, every subarray is valid and the answer isn * (n + 1) / 2. The sliding window never shrinks, so it counts all of them. - Product of entire array less than
k: The window expands across the whole array without ever shrinking. Every subarray is valid, and the total is againn * (n + 1) / 2.
From understanding to recall
You probably see the pattern now: expand right, shrink left while invalid, count right - left + 1. The logic is clean. But in an interview, the details trip people up. Do you divide or subtract when shrinking? Is it right - left + 1 or right - left? Do you need the k <= 1 guard?
Spaced repetition locks these details in. You write the solution from scratch today, then again tomorrow, then in a few days. After a few rounds, the while product >= k loop and the right - left + 1 counting formula are automatic. You do not need to re-derive them under pressure.
Related posts
- Minimum Size Subarray Sum - Another sliding window problem where you shrink to find the shortest valid window
- Longest Substring Without Repeating Characters - Classic sliding window with a set for tracking uniqueness
- Maximum Product Subarray - Another product-based array problem, though it uses dynamic programming instead of sliding window
If you want to build real fluency with sliding window and other patterns, CodeBricks uses spaced repetition to help you practice problems at the right intervals. Instead of grinding through hundreds of problems once, you revisit the ones that matter until the patterns stick.