2444. Count Subarrays With Fixed Bounds
Problem Description
You are given an integer array nums
and two integers minK
and maxK
.
A fixed-bound subarray is a contiguous portion of the array that satisfies two specific conditions:
- The minimum value in the subarray must be exactly equal to
minK
- The maximum value in the subarray must be exactly equal to
maxK
Your task is to count how many such fixed-bound subarrays exist in the given array.
For example, if nums = [1, 3, 5, 2, 7, 5]
, minK = 1
, and maxK = 5
:
- The subarray
[1, 3, 5]
is a fixed-bound subarray because its minimum is 1 (equalsminK
) and maximum is 5 (equalsmaxK
) - The subarray
[1, 3, 5, 2]
is also a fixed-bound subarray with minimum 1 and maximum 5 - The subarray
[3, 5]
is NOT a fixed-bound subarray because its minimum is 3 (not equal tominK = 1
)
The problem requires you to return the total count of all valid fixed-bound subarrays in the array.
Note that a subarray must be contiguous, meaning it consists of consecutive elements from the original array. Also, for a subarray to be valid, it must contain at least one occurrence of minK
and at least one occurrence of maxK
, and all elements must be within the range [minK, maxK]
.
Intuition
Instead of checking every possible subarray (which would be inefficient), we can think about this problem differently: for each position in the array, how many valid subarrays end at that position?
The key insight is that for a subarray ending at position i
to be valid:
- It must contain at least one occurrence of
minK
- It must contain at least one occurrence of
maxK
- It cannot contain any element outside the range
[minK, maxK]
This leads us to track three critical positions as we traverse the array:
- The most recent position where we saw
minK
(let's call itj1
) - The most recent position where we saw
maxK
(let's call itj2
) - The most recent position where we saw an invalid element (outside
[minK, maxK]
), let's call itk
For any position i
, valid subarrays ending at i
must:
- Start after position
k
(to avoid including invalid elements) - Start at or before
min(j1, j2)
(to ensure bothminK
andmaxK
are included)
Therefore, the number of valid subarrays ending at position i
is min(j1, j2) - k
(assuming this is positive; otherwise it's 0).
Why does this work? Because any starting position between k + 1
and min(j1, j2)
will create a valid subarray when paired with ending position i
. The subarray will contain both required values (minK
and maxK
) and no invalid elements.
By iterating through the array once and calculating the count for each position, we efficiently find the total number of fixed-bound subarrays in O(n)
time.
Learn more about Queue, Sliding Window and Monotonic Queue patterns.
Solution Approach
We enumerate each position as the right endpoint of potential subarrays and count how many valid subarrays end at that position.
Implementation Details:
-
Initialize tracking variables:
j1 = -1
: tracks the most recent index where we foundminK
j2 = -1
: tracks the most recent index where we foundmaxK
k = -1
: tracks the most recent index where we found an element outside[minK, maxK]
ans = 0
: accumulates the total count
-
Iterate through the array: For each element
nums[i]
:a. Check if element is invalid: If
v < minK
orv > maxK
, updatek = i
. This position marks a boundary - no valid subarray can span across this point.b. Update minimum position: If
v == minK
, updatej1 = i
to record the latest occurrence ofminK
.c. Update maximum position: If
v == maxK
, updatej2 = i
to record the latest occurrence ofmaxK
.d. Count valid subarrays ending at position i:
- The leftmost valid starting position is
k + 1
(just after the last invalid element) - The rightmost valid starting position is
min(j1, j2)
(to ensure bothminK
andmaxK
are included) - The number of valid subarrays ending at
i
ismax(0, min(j1, j2) - k)
- Add this count to
ans
- The leftmost valid starting position is
-
Return the result: After processing all elements,
ans
contains the total count of fixed-bound subarrays.
Why min(j1, j2) - k
works:
min(j1, j2)
ensures we have seen bothminK
andmaxK
before or at this position- Subtracting
k
gives us the count of valid starting positions - If
min(j1, j2) <= k
, it means we haven't seen both required values since the last invalid element, so no valid subarrays exist (hencemax(0, ...)
Time Complexity: O(n)
- single pass through the array
Space Complexity: O(1)
- only using a few variables to track positions
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [1, 3, 5, 2, 7, 5]
, minK = 1
, maxK = 5
.
We'll track three positions as we iterate:
j1
: last position ofminK
(1)j2
: last position ofmaxK
(5)k
: last position of invalid element (outside [1,5])ans
: running count of valid subarrays
Initial state: j1 = -1
, j2 = -1
, k = -1
, ans = 0
i = 0, nums[0] = 1:
- 1 is within [1,5] ✓
- 1 == minK, so update
j1 = 0
- min(j1, j2) = min(0, -1) = -1
- Valid subarrays ending here: max(0, -1 - (-1)) = 0
ans = 0
i = 1, nums[1] = 3:
- 3 is within [1,5] ✓
- 3 is neither minK nor maxK
- min(j1, j2) = min(0, -1) = -1
- Valid subarrays ending here: max(0, -1 - (-1)) = 0
ans = 0
i = 2, nums[2] = 5:
- 5 is within [1,5] ✓
- 5 == maxK, so update
j2 = 2
- min(j1, j2) = min(0, 2) = 0
- Valid subarrays ending here: max(0, 0 - (-1)) = 1
- This counts: [1,3,5] starting at index 0
ans = 1
i = 3, nums[3] = 2:
- 2 is within [1,5] ✓
- 2 is neither minK nor maxK
- min(j1, j2) = min(0, 2) = 0
- Valid subarrays ending here: max(0, 0 - (-1)) = 1
- This counts: [1,3,5,2] starting at index 0
ans = 2
i = 4, nums[4] = 7:
- 7 > maxK, invalid! Update
k = 4
- min(j1, j2) = min(0, 2) = 0
- Valid subarrays ending here: max(0, 0 - 4) = 0
- No valid subarrays (7 is outside range)
ans = 2
i = 5, nums[5] = 5:
- 5 is within [1,5] ✓
- 5 == maxK, so update
j2 = 5
- min(j1, j2) = min(0, 5) = 0
- Valid subarrays ending here: max(0, 0 - 4) = 0
- No valid subarrays (would need to include index 0 for minK, but index 4 has invalid element 7 in between)
ans = 2
Final answer: 2
The two valid fixed-bound subarrays are:
- [1, 3, 5] (indices 0-2)
- [1, 3, 5, 2] (indices 0-3)
Solution Implementation
1class Solution:
2 def countSubarrays(self, nums: List[int], minK: int, maxK: int) -> int:
3 # Track the most recent positions of key elements
4 last_min_pos = -1 # Last position where we found minK
5 last_max_pos = -1 # Last position where we found maxK
6 last_invalid_pos = -1 # Last position where we found an invalid element (outside [minK, maxK])
7
8 total_count = 0
9
10 # Iterate through each element in the array
11 for current_pos, value in enumerate(nums):
12 # Check if current element is outside the valid range [minK, maxK]
13 if value < minK or value > maxK:
14 last_invalid_pos = current_pos
15
16 # Update position if we found minK
17 if value == minK:
18 last_min_pos = current_pos
19
20 # Update position if we found maxK
21 if value == maxK:
22 last_max_pos = current_pos
23
24 # Calculate valid subarrays ending at current position
25 # We need both minK and maxK to appear after the last invalid element
26 # The number of valid subarrays is determined by the earlier occurrence of minK or maxK
27 leftmost_valid_start = min(last_min_pos, last_max_pos)
28 valid_subarray_count = max(0, leftmost_valid_start - last_invalid_pos)
29
30 total_count += valid_subarray_count
31
32 return total_count
33
1class Solution {
2 public long countSubarrays(int[] nums, int minK, int maxK) {
3 long totalCount = 0;
4
5 // Track the most recent positions of important elements
6 int lastMinPosition = -1; // Last position where we found minK
7 int lastMaxPosition = -1; // Last position where we found maxK
8 int lastInvalidPosition = -1; // Last position where we found an element outside [minK, maxK]
9
10 // Iterate through each element in the array
11 for (int currentIndex = 0; currentIndex < nums.length; currentIndex++) {
12 // Check if current element is outside the valid range [minK, maxK]
13 if (nums[currentIndex] < minK || nums[currentIndex] > maxK) {
14 lastInvalidPosition = currentIndex;
15 }
16
17 // Update position if we found minK
18 if (nums[currentIndex] == minK) {
19 lastMinPosition = currentIndex;
20 }
21
22 // Update position if we found maxK
23 if (nums[currentIndex] == maxK) {
24 lastMaxPosition = currentIndex;
25 }
26
27 // Calculate valid subarrays ending at current position
28 // We need both minK and maxK to appear after the last invalid element
29 // The number of valid subarrays is determined by the leftmost valid starting position
30 int leftmostValidStart = Math.min(lastMinPosition, lastMaxPosition);
31 int validSubarraysCount = Math.max(0, leftmostValidStart - lastInvalidPosition);
32
33 totalCount += validSubarraysCount;
34 }
35
36 return totalCount;
37 }
38}
39
1class Solution {
2public:
3 long long countSubarrays(vector<int>& nums, int minK, int maxK) {
4 long long result = 0;
5
6 // Track the most recent positions of key elements
7 int lastMinPosition = -1; // Last position where we found minK
8 int lastMaxPosition = -1; // Last position where we found maxK
9 int lastInvalidPosition = -1; // Last position where element is out of [minK, maxK] range
10
11 // Iterate through each element in the array
12 for (int currentIndex = 0; currentIndex < static_cast<int>(nums.size()); ++currentIndex) {
13 // Check if current element is outside the valid range [minK, maxK]
14 if (nums[currentIndex] < minK || nums[currentIndex] > maxK) {
15 lastInvalidPosition = currentIndex;
16 }
17
18 // Update position if we found minK
19 if (nums[currentIndex] == minK) {
20 lastMinPosition = currentIndex;
21 }
22
23 // Update position if we found maxK
24 if (nums[currentIndex] == maxK) {
25 lastMaxPosition = currentIndex;
26 }
27
28 // Calculate number of valid subarrays ending at current position
29 // A valid subarray must contain both minK and maxK, and no elements outside [minK, maxK]
30 // The leftmost valid starting position is (lastInvalidPosition + 1)
31 // The rightmost valid starting position is min(lastMinPosition, lastMaxPosition)
32 // Number of valid subarrays = rightmost - leftmost + 1 (if positive)
33 result += max(0, min(lastMinPosition, lastMaxPosition) - lastInvalidPosition);
34 }
35
36 return result;
37 }
38};
39
1/**
2 * Counts the number of subarrays where the minimum element is minK and maximum element is maxK
3 * @param nums - The input array of numbers
4 * @param minK - The required minimum value in valid subarrays
5 * @param maxK - The required maximum value in valid subarrays
6 * @returns The count of valid subarrays
7 */
8function countSubarrays(nums: number[], minK: number, maxK: number): number {
9 let totalCount: number = 0;
10
11 // Track the most recent positions of important elements
12 let lastMinPosition: number = -1; // Last position where we found minK
13 let lastMaxPosition: number = -1; // Last position where we found maxK
14 let lastInvalidPosition: number = -1; // Last position of element outside [minK, maxK] range
15
16 // Iterate through each element in the array
17 for (let currentIndex: number = 0; currentIndex < nums.length; currentIndex++) {
18 const currentValue: number = nums[currentIndex];
19
20 // If current element is outside the valid range, update the invalid boundary
21 if (currentValue < minK || currentValue > maxK) {
22 lastInvalidPosition = currentIndex;
23 }
24
25 // Update position if we found the minimum value
26 if (currentValue === minK) {
27 lastMinPosition = currentIndex;
28 }
29
30 // Update position if we found the maximum value
31 if (currentValue === maxK) {
32 lastMaxPosition = currentIndex;
33 }
34
35 // Calculate valid subarrays ending at current position
36 // The number of valid subarrays is determined by the distance between
37 // the earlier of (lastMinPosition, lastMaxPosition) and lastInvalidPosition
38 const earlierBoundPosition: number = Math.min(lastMinPosition, lastMaxPosition);
39 const validSubarraysCount: number = Math.max(0, earlierBoundPosition - lastInvalidPosition);
40
41 totalCount += validSubarraysCount;
42 }
43
44 return totalCount;
45}
46
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array exactly once using a single for loop with enumerate(nums)
. Each operation inside the loop (comparisons, assignments, and arithmetic operations) takes constant time O(1)
. Therefore, the overall time complexity is linear.
The space complexity is O(1)
. The algorithm uses only a fixed number of variables (j1
, j2
, k
, ans
, i
, and v
) regardless of the input size. No additional data structures that grow with the input are created, resulting in constant space usage.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Valid Range Check
Pitfall: A common mistake is incorrectly handling elements that equal minK
or maxK
when checking for invalid elements. Some might write:
# WRONG: This excludes minK and maxK themselves if value <= minK or value >= maxK: last_invalid_pos = current_pos
Why it's wrong: Elements equal to minK
and maxK
are valid and should NOT update last_invalid_pos
. Only elements strictly outside the range [minK, maxK]
are invalid.
Solution: Use strict inequality:
# CORRECT: Only mark as invalid if strictly outside [minK, maxK] if value < minK or value > maxK: last_invalid_pos = current_pos
2. Forgetting to Handle the Case When minK equals maxK
Pitfall: The code might fail or give incorrect results when minK == maxK
, which means we're looking for subarrays where all elements are the same value.
Why it matters: When minK == maxK
, both last_min_pos
and last_max_pos
get updated to the same value whenever we encounter this element, but the logic still works correctly.
Solution: The provided code actually handles this case correctly without modification because:
- When
value == minK == maxK
, both position trackers update to the same index - The calculation
min(last_min_pos, last_max_pos)
still gives the correct position - The counting logic remains valid
3. Off-by-One Error in Counting Valid Subarrays
Pitfall: Confusing the number of valid starting positions with indices:
# WRONG: This might seem intuitive but is incorrect valid_subarray_count = leftmost_valid_start - last_invalid_pos - 1
Why it's wrong: The number of valid starting positions from last_invalid_pos + 1
to leftmost_valid_start
(inclusive) is exactly leftmost_valid_start - last_invalid_pos
.
Solution: Remember that when counting positions from index a+1
to index b
(inclusive), the count is b - a
:
# CORRECT: Direct subtraction gives the count
valid_subarray_count = max(0, leftmost_valid_start - last_invalid_pos)
4. Not Initializing Position Trackers to -1
Pitfall: Initializing position trackers to 0:
# WRONG: Starting at 0 can cause incorrect counts last_min_pos = 0 last_max_pos = 0 last_invalid_pos = 0
Why it's wrong: If we haven't seen a required element yet, using 0 as the position would incorrectly suggest we've seen it at index 0, leading to wrong subarray counts.
Solution: Initialize all position trackers to -1 to indicate "not yet seen":
# CORRECT: -1 indicates we haven't encountered the element yet last_min_pos = -1 last_max_pos = -1 last_invalid_pos = -1
This ensures that min(last_min_pos, last_max_pos) - last_invalid_pos
correctly evaluates to a negative number (resulting in 0 after max(0, ...)
) when we haven't seen both required elements yet.
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Queue Intro Think of the last time you stood in line to buy movie tickets The first person in line gets their ticket first and any newcomers join the end of the line This system where the first person to arrive is the first to be served is a queue in real
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Monotonic Stack Deque Intro If you'd prefer a video div class responsive iframe iframe src https www youtube com embed Dq_ObZwTY_Q title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div The word monotonic means a list or
Want a Structured Path to Master System Design Too? Don’t Miss This!