Bird
Raised Fist0
Interview Prepknowing-what-to-trackmediumAmazonGoogleFacebook

Number of Subarrays with Bounded Maximum

Choose your preparation mode4 modes available
🎯
Number of Subarrays with Bounded Maximum
mediumHASH_MAPAmazonGoogleFacebook

Imagine you are analyzing user activity sessions, and you want to count how many continuous periods have a peak activity level within a certain range.

💡 This problem requires understanding how to efficiently count subarrays that satisfy a maximum constraint. Beginners often struggle because naive methods check all subarrays, which is too slow, and they don't know how to track boundaries or last invalid positions to optimize counting.
📋
Problem Statement

Given an integer array nums and two integers left and right, return the number of contiguous non-empty subarrays such that the maximum element in that subarray is at least left and at most right.

1 ≤ nums.length ≤ 10^50 ≤ nums[i] ≤ 10^90 ≤ left ≤ right ≤ 10^9
💡
Example
Input"nums = [2,1,4,3], left = 2, right = 3"
Output3

The subarrays are [2], [2,1], and [3]. Each has max between 2 and 3.

Input"nums = [2,9,2,5,6], left = 2, right = 8"
Output7

Valid subarrays include [2], [2,9] is invalid because 9 > 8, so excluded. Counting all valid subarrays yields 7.

  • All elements less than left → output 0
  • All elements greater than right → output 0
  • Single element array within bounds → output 1
  • Array with all elements equal to left or right → output count of all subarrays
⚠️
Common Mistakes
Not resetting last_invalid when element > right

Counts invalid subarrays leading to incorrect total

✅ Update last_invalid index whenever nums[i] > right

Counting subarrays with max < left as valid

Overcounts subarrays that don't meet minimum max requirement

✅ Track last_valid index where element is within [left, right] and count accordingly

Using max function inside nested loops in brute force without caching

Leads to O(n^3) time, causing timeouts

✅ Update max incrementally in inner loop instead of recomputing

Off-by-one errors in counting valid subarrays ending at index

Incorrect counts, often off by one or zero

✅ Use max(0, last_valid - last_invalid) to count valid subarrays ending at current index

Confusing subarray max with subarray sum

Wrong approach and incorrect results

✅ Focus on max element tracking, not sum

🧠
Brute Force (Nested Loops Checking Each Subarray)
💡 This approach exists to build intuition by directly checking every possible subarray. It is simple but inefficient, helping beginners understand the problem's requirements before optimizing.

Intuition

Check every subarray, find its maximum, and count it if the max is within [left, right].

Algorithm

  1. Initialize count to 0.
  2. For each start index i in nums:
  3. For each end index j from i to end:
  4. Find max element in nums[i..j].
  5. If max is between left and right, increment count.
💡 The nested loops and max calculation are straightforward but lead to repeated work and inefficiency.
</>
Code
def numSubarrayBoundedMax(nums, left, right):
    count = 0
    n = len(nums)
    for i in range(n):
        current_max = float('-inf')
        for j in range(i, n):
            current_max = max(current_max, nums[j])
            if left <= current_max <= right:
                count += 1
    return count

# Driver code
if __name__ == '__main__':
    print(numSubarrayBoundedMax([2,1,4,3], 2, 3))  # Output: 3
Line Notes
count = 0Initialize counter to accumulate valid subarrays
for i in range(n)Outer loop picks start index of subarray
current_max = float('-inf')Reset max for new subarray starting at i
for j in range(i, n)Inner loop extends subarray to end index j
current_max = max(current_max, nums[j])Update max element in current subarray
if left <= current_max <= rightCheck if current max is within bounds to count
public class Solution {
    public static int numSubarrayBoundedMax(int[] nums, int left, int right) {
        int count = 0;
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            int currentMax = Integer.MIN_VALUE;
            for (int j = i; j < n; j++) {
                currentMax = Math.max(currentMax, nums[j]);
                if (currentMax >= left && currentMax <= right) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(numSubarrayBoundedMax(new int[]{2,1,4,3}, 2, 3)); // 3
    }
}
Line Notes
int count = 0;Initialize count of valid subarrays
for (int i = 0; i < n; i++)Outer loop selects start index
int currentMax = Integer.MIN_VALUE;Reset max for new subarray
for (int j = i; j < n; j++)Inner loop selects end index
currentMax = Math.max(currentMax, nums[j]);Update max in current subarray
if (currentMax >= left && currentMax <= right)Check if max is within bounds
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
    int count = 0;
    int n = nums.size();
    for (int i = 0; i < n; i++) {
        int currentMax = INT_MIN;
        for (int j = i; j < n; j++) {
            currentMax = max(currentMax, nums[j]);
            if (currentMax >= left && currentMax <= right) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    vector<int> nums = {2,1,4,3};
    cout << numSubarrayBoundedMax(nums, 2, 3) << endl; // 3
    return 0;
}
Line Notes
int count = 0;Initialize count accumulator
for (int i = 0; i < n; i++)Outer loop for subarray start
int currentMax = INT_MIN;Reset max for each start index
for (int j = i; j < n; j++)Inner loop for subarray end
currentMax = max(currentMax, nums[j]);Update max element in subarray
if (currentMax >= left && currentMax <= right)Check if max is valid
function numSubarrayBoundedMax(nums, left, right) {
    let count = 0;
    const n = nums.length;
    for (let i = 0; i < n; i++) {
        let currentMax = -Infinity;
        for (let j = i; j < n; j++) {
            currentMax = Math.max(currentMax, nums[j]);
            if (currentMax >= left && currentMax <= right) {
                count++;
            }
        }
    }
    return count;
}

// Test
console.log(numSubarrayBoundedMax([2,1,4,3], 2, 3)); // 3
Line Notes
let count = 0;Initialize count of valid subarrays
for (let i = 0; i < n; i++)Outer loop picks subarray start
let currentMax = -Infinity;Reset max for each new start
for (let j = i; j < n; j++)Inner loop picks subarray end
currentMax = Math.max(currentMax, nums[j]);Update max in current subarray
if (currentMax >= left && currentMax <= right)Check if max is within bounds
⏱
Complexity
TimeO(n^2)
SpaceO(1)

Two nested loops each up to n, and max updated in O(1) per iteration, total O(n^2).

💡 For n=20, this means about 400 operations, which is okay, but for n=10^5 it is 10^10 operations, too slow.
Interview Verdict: TLE for large inputs

This approach is too slow for large inputs but is useful to understand the problem and correctness before optimizing.

🧠
Better Approach (Track Last Invalid and Count Valid Subarrays Ending at Each Index)
💡 This approach introduces the key insight of tracking the last index where the subarray became invalid, allowing counting valid subarrays ending at each position in O(n).

Intuition

Keep track of the last index where an element exceeded right (invalid), and count how many subarrays ending at current index have max within bounds.

Algorithm

  1. Initialize count, last_invalid = -1, last_valid = -1.
  2. Iterate over nums with index i:
  3. If nums[i] > right, update last_invalid = i (subarray invalid beyond this).
  4. If nums[i] >= left and nums[i] <= right, update last_valid = i (valid max found).
  5. Add max(0, last_valid - last_invalid) to count (number of valid subarrays ending at i).
  6. Return count.
💡 The key is to track two pointers: last invalid and last valid max, to count valid subarrays ending at each index without checking all subarrays.
</>
Code
def numSubarrayBoundedMax(nums, left, right):
    count = 0
    last_invalid = -1
    last_valid = -1
    for i, num in enumerate(nums):
        if num > right:
            last_invalid = i
        if left <= num <= right:
            last_valid = i
        count += max(0, last_valid - last_invalid)
    return count

# Driver code
if __name__ == '__main__':
    print(numSubarrayBoundedMax([2,1,4,3], 2, 3))  # Output: 3
Line Notes
count = 0Initialize total count of valid subarrays
last_invalid = -1Track last index where element > right (invalid)
last_valid = -1Track last index where element in [left, right]
for i, num in enumerate(nums)Iterate over array with index and value
if num > right: last_invalid = iUpdate last invalid index when element too big
if left <= num <= right: last_valid = iUpdate last valid max index
count += max(0, last_valid - last_invalid)Add count of valid subarrays ending at i
public class Solution {
    public static int numSubarrayBoundedMax(int[] nums, int left, int right) {
        int count = 0;
        int lastInvalid = -1;
        int lastValid = -1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > right) lastInvalid = i;
            if (nums[i] >= left && nums[i] <= right) lastValid = i;
            count += Math.max(0, lastValid - lastInvalid);
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(numSubarrayBoundedMax(new int[]{2,1,4,3}, 2, 3)); // 3
    }
}
Line Notes
int count = 0;Initialize count accumulator
int lastInvalid = -1;Track last invalid index where element > right
int lastValid = -1;Track last valid max index within bounds
for (int i = 0; i < nums.length; i++)Iterate over array indices
if (nums[i] > right) lastInvalid = i;Update last invalid index if element too large
if (nums[i] >= left && nums[i] <= right) lastValid = i;Update last valid max index
count += Math.max(0, lastValid - lastInvalid);Count valid subarrays ending at i
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
    int count = 0;
    int lastInvalid = -1;
    int lastValid = -1;
    for (int i = 0; i < (int)nums.size(); i++) {
        if (nums[i] > right) lastInvalid = i;
        if (nums[i] >= left && nums[i] <= right) lastValid = i;
        count += max(0, lastValid - lastInvalid);
    }
    return count;
}

int main() {
    vector<int> nums = {2,1,4,3};
    cout << numSubarrayBoundedMax(nums, 2, 3) << endl; // 3
    return 0;
}
Line Notes
int count = 0;Initialize count of valid subarrays
int lastInvalid = -1;Track last invalid index where element > right
int lastValid = -1;Track last valid max index within bounds
for (int i = 0; i < (int)nums.size(); i++)Iterate over array indices
if (nums[i] > right) lastInvalid = i;Update last invalid index if element too large
if (nums[i] >= left && nums[i] <= right) lastValid = i;Update last valid max index
count += max(0, lastValid - lastInvalid);Count valid subarrays ending at i
function numSubarrayBoundedMax(nums, left, right) {
    let count = 0;
    let lastInvalid = -1;
    let lastValid = -1;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] > right) lastInvalid = i;
        if (nums[i] >= left && nums[i] <= right) lastValid = i;
        count += Math.max(0, lastValid - lastInvalid);
    }
    return count;
}

// Test
console.log(numSubarrayBoundedMax([2,1,4,3], 2, 3)); // 3
Line Notes
let count = 0;Initialize count accumulator
let lastInvalid = -1;Track last invalid index where element > right
let lastValid = -1;Track last valid max index within bounds
for (let i = 0; i < nums.length; i++)Iterate over array indices
if (nums[i] > right) lastInvalid = i;Update last invalid index if element too large
if (nums[i] >= left && nums[i] <= right) lastValid = i;Update last valid max index
count += Math.max(0, lastValid - lastInvalid);Count valid subarrays ending at i
⏱
Complexity
TimeO(n)
SpaceO(1)

Single pass through array with constant extra space, counting valid subarrays efficiently.

💡 For n=10^5, this means about 100,000 operations, which is efficient and scalable.
Interview Verdict: Accepted

This approach is optimal and passes all tests efficiently, demonstrating mastery of tracking boundaries.

🧠
Alternative Optimal Approach (Count Subarrays with Max ≤ right and Subtract Those with Max < left)
💡 This approach uses the principle of inclusion-exclusion by counting subarrays with max ≤ right and subtracting those with max < left, leveraging a helper function for counting.

Intuition

Count all subarrays where max ≤ right, then subtract subarrays where max < left, leaving subarrays where max is in [left, right].

Algorithm

  1. Define a helper function countAtMost(bound) that counts subarrays with max ≤ bound.
  2. Compute countRight = countAtMost(right).
  3. Compute countLeft = countAtMost(left - 1).
  4. Return countRight - countLeft.
💡 The helper function counts valid subarrays by tracking stretches of elements ≤ bound, then difference isolates subarrays with max in [left, right].
</>
Code
def countAtMost(nums, bound):
    count = 0
    length = 0
    for num in nums:
        if num <= bound:
            length += 1
            count += length
        else:
            length = 0
    return count

def numSubarrayBoundedMax(nums, left, right):
    return countAtMost(nums, right) - countAtMost(nums, left - 1)

# Driver code
if __name__ == '__main__':
    print(numSubarrayBoundedMax([2,1,4,3], 2, 3))  # Output: 3
Line Notes
def countAtMost(nums, bound):Helper counts subarrays with max ≤ bound
length = 0Tracks current length of valid subarray segment
if num <= bound: length += 1Extend current valid segment if element ≤ bound
count += lengthAdd number of new subarrays ending at current element
else: length = 0Reset length when element exceeds bound
return countReturn total count of valid subarrays
return countAtMost(nums, right) - countAtMost(nums, left - 1)Difference gives count of subarrays with max in [left, right]
public class Solution {
    private static int countAtMost(int[] nums, int bound) {
        int count = 0, length = 0;
        for (int num : nums) {
            if (num <= bound) {
                length++;
                count += length;
            } else {
                length = 0;
            }
        }
        return count;
    }

    public static int numSubarrayBoundedMax(int[] nums, int left, int right) {
        return countAtMost(nums, right) - countAtMost(nums, left - 1);
    }

    public static void main(String[] args) {
        System.out.println(numSubarrayBoundedMax(new int[]{2,1,4,3}, 2, 3)); // 3
    }
}
Line Notes
int count = 0, length = 0;Initialize counters for valid subarrays and current segment length
for (int num : nums)Iterate over array elements
if (num <= bound)Check if element fits bound to extend segment
length++; count += length;Extend segment and add new subarrays ending here
else length = 0;Reset segment length if element too large
return count;Return total count of valid subarrays
return countAtMost(nums, right) - countAtMost(nums, left - 1);Subtract counts to isolate subarrays with max in [left, right]
#include <iostream>
#include <vector>
using namespace std;

int countAtMost(const vector<int>& nums, int bound) {
    int count = 0, length = 0;
    for (int num : nums) {
        if (num <= bound) {
            length++;
            count += length;
        } else {
            length = 0;
        }
    }
    return count;
}

int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
    return countAtMost(nums, right) - countAtMost(nums, left - 1);
}

int main() {
    vector<int> nums = {2,1,4,3};
    cout << numSubarrayBoundedMax(nums, 2, 3) << endl; // 3
    return 0;
}
Line Notes
int count = 0, length = 0;Initialize counters for valid subarrays and segment length
for (int num : nums)Iterate over elements
if (num <= bound)Check if element fits bound to extend segment
length++; count += length;Extend segment and add new subarrays ending here
else length = 0;Reset segment length if element too large
return count;Return total count of valid subarrays
return countAtMost(nums, right) - countAtMost(nums, left - 1);Subtract counts to isolate subarrays with max in [left, right]
function countAtMost(nums, bound) {
    let count = 0, length = 0;
    for (const num of nums) {
        if (num <= bound) {
            length++;
            count += length;
        } else {
            length = 0;
        }
    }
    return count;
}

function numSubarrayBoundedMax(nums, left, right) {
    return countAtMost(nums, right) - countAtMost(nums, left - 1);
}

// Test
console.log(numSubarrayBoundedMax([2,1,4,3], 2, 3)); // 3
Line Notes
let count = 0, length = 0;Initialize counters for valid subarrays and segment length
for (const num of nums)Iterate over array elements
if (num <= bound)Check if element fits bound to extend segment
length++; count += length;Extend segment and add new subarrays ending here
else length = 0;Reset segment length if element too large
return count;Return total count of valid subarrays
return countAtMost(nums, right) - countAtMost(nums, left - 1);Subtract counts to isolate subarrays with max in [left, right]
⏱
Complexity
TimeO(n)
SpaceO(1)

Two passes through the array with constant extra space, counting subarrays efficiently.

💡 For n=10^5, this approach is very efficient and easy to implement.
Interview Verdict: Accepted

This approach is elegant and optimal, showing mastery of problem decomposition and counting techniques.

📊
All Approaches - One-Glance Tradeoffs
💡 In interviews, code the second approach (tracking last invalid and valid indices) for clarity and efficiency.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n^2)O(1)NoN/AMention only - never code due to inefficiency
2. Track Last Invalid and Valid IndicesO(n)O(1)NoN/ACode this approach for optimal solution
3. Inclusion-Exclusion CountingO(n)O(1)NoN/AMention as alternative or if interviewer asks for different perspective
💼
Interview Strategy
💡 Use this guide to understand the problem deeply before interviews. Start by clarifying the problem, then explain brute force, and progressively optimize. Practice coding and testing each approach.

How to Present

Step 1: Clarify the problem and constraints with the interviewer.Step 2: Describe the brute force approach and its inefficiency.Step 3: Introduce the idea of tracking last invalid and valid indices for O(n) solution.Step 4: Optionally mention the inclusion-exclusion counting approach.Step 5: Code the optimal approach and test with examples.

Time Allocation

Clarify: 3min → Approach: 5min → Code: 10min → Test: 5min. Total ~23min

What the Interviewer Tests

The interviewer tests your ability to identify inefficiencies, track boundaries correctly, and implement an O(n) solution that counts valid subarrays without enumerating all.

Common Follow-ups

  • What if we want subarrays with max exactly equal to a value? → Modify tracking logic accordingly.
  • Can you count subarrays with sum constraints instead? → Different approach, often prefix sums.
💡 These follow-ups test your flexibility in adapting the counting and tracking techniques to related problems.
🔍
Pattern Recognition

When to Use

1) Asked to count subarrays with constraints on max element; 2) Constraints involve bounds on max; 3) Need efficient O(n) solution; 4) Problem hints at tracking last invalid or valid indices.

Signature Phrases

'maximum element in that subarray is at least left and at most right''count the number of contiguous subarrays satisfying bounds'

NOT This Pattern When

Problems focusing on subarray sums or products, which require prefix sums or segment trees instead.

Similar Problems

Longest Substring Without Repeating Characters - tracking last invalid index to maintain constraintsCount Number of Nice Subarrays - counting subarrays with exactly k odd numbers using similar counting techniquesSubarrays with K Different Integers - sliding window and counting valid subarrays

Practice

(1/5)
1. You are given an integer array and a number k. You need to count the number of contiguous subarrays that contain exactly k odd numbers. Which approach guarantees an optimal solution with linear time complexity?
easy
A. Use dynamic programming to store counts of subarrays ending at each index with exactly k odd numbers.
B. Use a brute force nested loop to check all subarrays and count odd numbers.
C. Use a sliding window with two pointers to count subarrays with at most k and k-1 odd numbers and subtract.
D. Use a greedy approach to expand and shrink the window whenever the odd count exceeds k.

Solution

  1. Step 1: Understand the problem constraints

    The problem requires counting subarrays with exactly k odd numbers efficiently.
  2. Step 2: Evaluate approaches

    Brute force is O(n^2), DP is complex and not straightforward here, greedy alone doesn't guarantee correctness. Sliding window with two pointers counting at most k and k-1 odd numbers and subtracting is optimal and linear.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Sliding window approach runs in O(n) time [OK]
Hint: Sliding window with at most k counts is optimal [OK]
Common Mistakes:
  • Thinking greedy alone solves it optimally
  • Assuming DP is needed
  • Using brute force for large inputs
2. Consider the following code snippet for finding the longest subarray with equal number of 0s and 1s. What is the value of max_length after processing the input [0, 1, 0, 1]?
easy
A. 3
B. 4
C. 2
D. 1

Solution

  1. Step 1: Trace prefix sums and first_occurrence updates.

    For input [0,1,0,1], prefix sums are: -1, 0, -1, 0 with offset indexing.
  2. Step 2: Update max_length when prefix sum repeats.

    At i=1, prefix_sum=0 repeats at index -1 -> length=2; at i=3, prefix_sum=0 repeats again -> length=4.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Longest balanced subarray is entire array length 4 [OK]
Hint: Prefix sum repeats indicate balanced subarray [OK]
Common Mistakes:
  • Off-by-one in length calculation
  • Confusing prefix sum indexing
  • Ignoring initial prefix sum at -1
3. Given the following Python code for summaryRanges, what is the final returned list when input is [1,2,3,5]?
from typing import List

def summaryRanges(nums: List[int]) -> List[str]:
    ranges = []
    n = len(nums)
    start = 0
    while start < n:
        end = start
        while end + 1 < n and nums[end + 1] == nums[end] + 1:
            end += 1
        if start == end:
            ranges.append(str(nums[start]))
        else:
            ranges.append(f"{nums[start]}->{nums[end]}")
        start = end + 1
    return ranges
easy
A. ['1', '2', '3', '5']
B. ['1->3', '5']
C. ['1->2', '3->5']
D. ['1->3', '5->5']

Solution

  1. Step 1: Trace first range detection

    start=0 (nums[0]=1), end moves from 0 to 2 because nums[1]=2 and nums[2]=3 are consecutive.
  2. Step 2: Append first range and move start

    Append '1->3', then start=3 (nums[3]=5), end=3, no consecutive next element, append '5'.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Ranges correctly summarize consecutive sequences [OK]
Hint: Two pointers expand end while consecutive -> correct ranges [OK]
Common Mistakes:
  • Appending single elements as ranges like '5->5'
  • Splitting consecutive sequences incorrectly
  • Off-by-one errors in inner loop
4. What is the time complexity of the optimal single-pass pivot index algorithm that uses a running left sum and total sum?
medium
A. O(n) because it iterates through the array once, updating sums in constant time.
B. O(n^2) because of nested sum computations inside the loop.
C. O(n log n) due to sorting the array before processing.
D. O(n) time but O(n) space due to storing prefix sums.

Solution

  1. Step 1: Analyze loop iterations

    The algorithm iterates through the array once, performing constant time operations per element.
  2. Step 2: Confirm no nested loops or extra space

    There are no nested loops or sorting; space used is O(1) for running sums.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Single pass with constant time per element -> O(n) [OK]
Hint: Single loop with constant work per iteration -> O(n) [OK]
Common Mistakes:
  • Confusing with brute force O(n^2)
  • Assuming sorting is needed
  • Forgetting space is O(1) not O(n)
5. The following code attempts to implement the optimized sliding window approach for minimum window substring. Identify the line containing the subtle bug that causes incorrect results on some inputs.
medium
A. Line assigning min_window = s[l:r+1]
B. Line incrementing formed when window_count[c] == t_count[c]
C. Line decrementing window_count[filtered_s[l][1]] inside while loop
D. Line filtering characters into filtered_s

Solution

  1. Step 1: Identify substring extraction logic

    The code uses indices l and r as pointers over filtered_s, not original string s. Using s[l:r+1] extracts substring from index l to r in s, which is incorrect because l and r are indices in filtered_s, not s.
  2. Step 2: Correct substring extraction

    Correct substring should use filtered_s[l][0] and filtered_s[r][0] to map back to original string indices: s[filtered_s[l][0]:filtered_s[r][0]+1].
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Incorrect substring indexing causes wrong or runtime errors [OK]
Hint: Check if substring indices map to original string [OK]
Common Mistakes:
  • Using filtered indices directly on original string
  • Not updating formed count correctly
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard