LeetCode/区间子数组个数
给你一个整数数组 nums 和两个整数:left 及 right
找出 nums 中连续、非空且其中最大元素在范围 [left, right] 内的子数组,并返回满足条件的子数组的个数
1. 遍历区间右端点 + 同时记录满足条件的左边点位
数组中不能含有大于 right的元素,
且至少含有一个处于 [left,right]区间的元素
所以遍历时只需记录最近不满足条件的点位
和最近满足条件的点位即可,可以将其之间的元素,作为新增右端点的区间左端点
class Solution {
public:
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
int res = 0, last2 = -1, last1 = -1;
for (int i = 0; i < nums.size(); i++) { //直接遍历维护右端点
if (nums[i] >= left && nums[i] <= right)
last1 = i; //更新记录满足条件的最远点1
else if (nums[i] > right) {
last2 = i; //更新记录不满足条件的最远点2
last1 = -1; //当前数为点2则不存在区间满足条件
}
//
if (last1 != -1)
res += last1 - last2;
}
return res;
}
};
2. 转化问题
所有元素小于等于right数组 - 所有元素小于left数组
class Solution {
public:
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
return count(nums, right) - count(nums, left - 1);
}
int count(vector<int>& nums, int lower) {
int res = 0, cur = 0; //cur记录连续满足条件的长度
for (auto x : nums) { //以x为右端点
cur = x <= lower ? cur + 1 : 0; //满足条件增加,否则重置
res += cur;
}
return res;
}
};
3,. 区间单调栈板子
class Solution {
public:
int numSubarrayBoundedMax(vector<int>& arr, int l, int r) {
int n = arr.size();
vector<int> monoStack; //单调栈
vector<int> left(n), right(n);//左右边界,表示以i最大值的左右边界
for (int i = 0; i < n; i++) {//从左往右找左边更大值
while (!monoStack.empty() && arr[i] >= arr[monoStack.back()]) {//把更大的值挤出来,相等的视为更大(即最右边值统御左边相同元素)
monoStack.pop_back();
}
left[i] = i - (monoStack.empty() ? -1 : monoStack.back()); //左区间长度
monoStack.emplace_back(i);//放入坐标
}
monoStack.clear();
for (int i = n - 1; i >= 0; i--) { //从右往左找右边更大值
while (!monoStack.empty() && arr[i] > arr[monoStack.back()]) {
monoStack.pop_back();
}
right[i] = (monoStack.empty() ? n : monoStack.back()) - i; //右区间长度
monoStack.emplace_back(i);
}
int ans = 0;
for (int i = 0; i < n; i++){ //计算每个值的贡献
if(arr[i]>r||arr[i]<l) continue;
ans = ans + left[i] * right[i];
}
return ans;
}
};