You are given an integer array nums
and two integers left
and right
. A subarray of nums
is a contiguous, non-empty sequence of elements within nums
. The maximum of a subarray is the largest value present in that subarray.
Your task is to count the number of subarrays whose maximum element is at least left
and at most right
; in other words, the maximum lies in the interval [left, right]
.
nums
.Constraints:
1 <= nums.length <= 10^5
0 <= nums[i] <= 10^9
0 <= left <= right <= 10^9
The most straightforward way to solve this problem is to consider every possible subarray, compute its maximum, and count it if the maximum is between left
and right
. However, this brute-force approach is not efficient for large arrays because it requires examining all O(n^2)
subarrays.
To optimize, we can think about the properties of subarrays and how the maximum value changes as the subarray expands. Instead of checking each subarray individually, we can focus on efficiently counting subarrays that meet the maximum condition by leveraging the position of elements greater than right
and those within [left, right]
.
The key insight is that any subarray that contains an element greater than right
cannot be valid, so we can partition the array into blocks separated by such elements. Within each block, we can count subarrays whose maximum is at least left
by counting those that contain at least one element in [left, right]
.
Here is a step-by-step approach to solving the problem efficiently:
right
as a boundary. Any subarray containing such an element cannot be valid. So, the array can be split into blocks of elements where all elements are less than or equal to right
.
left
: For each block, we want to count the number of subarrays where the maximum is at least left
. This is equivalent to counting all subarrays in the block, then subtracting those where all elements are less than left
(i.e., the maximum is less than left
).
len
. The total number of subarrays is len * (len + 1) / 2
.left
, find all consecutive runs of elements less than left
in the block. For each such run of length l
, there are l * (l + 1) / 2
subarrays.nums
once. This ensures an efficient O(n)
solution.
This approach leverages the observation that the problem can be broken down into manageable segments, and counting is done mathematically rather than by enumeration.
Example:
nums = [2, 1, 4, 3]
, left = 2
, right = 3
right
(which is 3). The element 4 at index 2 is such a boundary.
[2, 1]
[3]
[2, 1]
):
[2]
, [1]
, [2,1]
)[1]
(length 1 run). 1 * 2 / 2 = 1[2]
, [2,1]
)[3]
):
[3]
)nums
.
The key to solving the "Number of Subarrays with Bounded Maximum" problem efficiently is to recognize how elements greater than right
partition the array and how to count valid subarrays mathematically within each partition. By subtracting subarrays whose maximum is less than left
from all possible subarrays in each block, we avoid brute-force enumeration and achieve a linear-time solution. This approach is both elegant and highly efficient, suitable for large input sizes.
class Solution:
def numSubarrayBoundedMax(self, nums, left, right):
def count(bound):
ans = 0
curr = 0
for num in nums:
if num <= bound:
curr += 1
else:
curr = 0
ans += curr
return ans
return count(right) - count(left - 1)
class Solution {
public:
int count(vector<int>& nums, int bound) {
int ans = 0, curr = 0;
for (int num : nums) {
if (num <= bound) {
curr++;
} else {
curr = 0;
}
ans += curr;
}
return ans;
}
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
return count(nums, right) - count(nums, left - 1);
}
};
class Solution {
private int count(int[] nums, int bound) {
int ans = 0, curr = 0;
for (int num : nums) {
if (num <= bound) {
curr++;
} else {
curr = 0;
}
ans += curr;
}
return ans;
}
public int numSubarrayBoundedMax(int[] nums, int left, int right) {
return count(nums, right) - count(nums, left - 1);
}
}
var numSubarrayBoundedMax = function(nums, left, right) {
function count(bound) {
let ans = 0, curr = 0;
for (let num of nums) {
if (num <= bound) {
curr++;
} else {
curr = 0;
}
ans += curr;
}
return ans;
}
return count(right) - count(left - 1);
};