Problem
Given an array of integers and two bounds minK and maxK, count how many subarrays have a minimum value of exactly minK and a maximum value of exactly maxK. Every element in such a subarray must fall within [minK, maxK], with the smallest element hitting minK exactly and the largest hitting maxK exactly.
- Input:
nums = [1, 3, 5, 2, 7, 5],minK = 1,maxK = 5 - Output:
2 - Explanation:
[1, 3, 5]and[1, 3, 5, 2]each have min = 1 and max = 5; the subarray[3, 5, 2]fails because its minimum is 2, not 1
Counter-example: [1, 3, 5, 2, 7, 5] โ once index 4 (value 7) appears, any subarray containing it is automatically disqualified because 7 > maxK = 5.
Intuition
A valid subarray must include at least one minK and one maxK, and cannot contain any element outside [minK, maxK]. The key observation: for any right endpoint, the leftmost valid start is constrained by two things โ it must come after the last out-of-range element (which breaks the window entirely), and it must be at or before the earlier of the last minK and last maxK positions (to guarantee both bounds are covered). Counting valid left starts is then a single arithmetic expression per element.
Solution โ Tracked-Index Sliding Window
For each position right, maintain three indices: the last position of an out-of-range element, the last position of minK, and the last position of maxK. The number of valid subarrays ending at right is the count of valid left starting points, which lies in the range (bad_idx, min(last_min, last_max)].
- Initialize
bad_idx = -1,last_min = -1,last_max = -1,result = 0 - For each index
right, examinenums[right]: - If the value falls outside
[minK, maxK], updatebad_idx = rightโ this element cannot appear in any valid subarray - If the value equals
minK, updatelast_min = right; if it equalsmaxK, updatelast_max = right - Add
max(0, min(last_min, last_max) - bad_idx)toresultโ this counts valid left endpoints - Return
result
1def countSubarrays(self, nums: list[int], minK: int, maxK: int) -> int:
2 result = 0
3 bad_idx = -1 # last position of a value outside [minK, maxK]
4 last_min = -1 # last position where the value equals minK
5 last_max = -1 # last position where the value equals maxK
6
7 for right, value in enumerate(nums):
8 if value < minK or value > maxK:
9 bad_idx = right # any subarray spanning this index is disqualified
10 if value == minK:
11 last_min = right
12 if value == maxK:
13 last_max = right
14 # left boundary can start anywhere from bad_idx+1 to min(last_min, last_max)
15 # giving min(last_min, last_max) - bad_idx valid starting positions
16 result += max(0, min(last_min, last_max) - bad_idx)
17
18 return resultTime: O(n) โ a single pass with O(1) work per element
Space: O(1) โ only three index variables regardless of input size
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Tracked-Index Sliding Window | O(n) | O(1) | Always โ this is the canonical O(n) solution |
Common Mistakes
- Misunderstanding what
bad_idxresets: an out-of-range element does not just shrink the window from the left โ it completely invalidates any subarray containing it.bad_idxmust be updated to the element's position, not tobad_idx + 1. - Off-by-one in the count formula: valid left starts run from
bad_idx + 1throughmin(last_min, last_max)inclusive, which is exactlymin(last_min, last_max) - bad_idxpositions. Subtractingbad_idx + 1gives one fewer โ a common error when converting the range to a count. - Assuming uninitialized indices cause bugs: initializing
last_min = last_max = bad_idx = -1is intentional. When neither minK nor maxK has been seen yet,min(-1, -1) - (-1) = 0, so no subarrays are counted. The math self-corrects without special-casing. - Forgetting
longfor the return type in Java/C++: the answer can reach O(nยฒ) in magnitude โ fornums = [minK, maxK, minK, maxK, ...]of length 10โต, the count approaches 10ยนโฐ, overflowing a 32-bit integer. - Missing the minK == maxK case: when both bounds are equal, the same element satisfies both
ifbranches simultaneously, updating bothlast_minandlast_maxin a single step. The solution handles this correctly without special logic, but it's easy to accidentally add anelse ifthat breaks it.
Related Problems
sliding-window-maximumโ tracks extreme values within a window using a deque, the structure that underlies many fixed-window problemsminimum-window-substringโ counts valid starting positions after satisfying character-frequency constraints, the same "count valid lefts" patternmax-consecutive-ones-iiiโ sliding window that tracks a disqualifying condition (too many zeros), analogous tobad_idxsubarray-sum-equals-kโ counting subarrays satisfying an exact numeric condition, different technique but same goallongest-repeating-character-replacementโ sliding window with a two-sided constraint on what qualifies as a valid window