一、问题描述
给你一个整数数组 nums 和 两个整数:left 及 right 。找出 nums 中连续、非空且其中最大元素在范围 [left, right] 内的子数组,并返回满足条件的子数组的个数。
示例:
输入:nums = [ 2, 1, 4, 3] ; left = 2; right = 2;
输出:3;
解释:根据题意,满足的子区间有 [2], [2,1], [3] 共三个;
二、解题思路
1、思路1:固定左端点,从左往右查找
根据题意,不难发现所求的子区间总是被 “大于 right 的数” 完全分割成互不相交的一个个“一级子区间”,然后在这些子区间中,分割出所求的各个相互之间存在交集的“二级子区间”,这个“二级子区间”的数量即为最终所求,“二级子区间”数量符合一定的规律:假如这个端点介于 [left, right] 之间,那么以他为首,1、2、3、4……为长度向后获取子数组,直到右边界,得到的数量即为这个首部对应的子数组数量,即右边界与他的距离即为所求;倘若这个值小于 left,那么他退化为与下一个(右边)介于 [left, right] 之间的数开头相同(注意这里的“查找下一个”)。
例如,给定 nums = [2, 1, 1, 2, 4, 3],left = 2,right = 3,求解如下:
(1)根据大于right的数,进行一级分割:
大于right的数为4,这个分割点将nums分割为两个“一级子区间”: [2, 1, 1, 2] 和 [3];
(2)对这两个区间,分别求他们的满足题意的区间数,求解时固定左端点向右查找:
1>对于区间 [2, 1, 1, 2]:
以第一个 2 开头的区间均满足题意,包括 [2], [2, 1], [2, 1, 1], [2, 1, 1, 2] 共4个;
以第一个 1 开头的区间,需要向后包括一个大于 left 的数,即向后查到 [1, 1, 2]共1个;
以第二个 1 开头的区间与上面相同,向后查到 [1, 2]共1个;
以第二个 2 开头的区间,与第一个相同,但是后面无元素,向后查到 [2] 共1个;
2>对于区间 [3]:
易知只有一个 [3] 区间满足题意;
综上 1> 2>,共有8个符合题意的子区间。
2、思路2:固定右端点,从右往左查找
分析思路1的过程,有一部分时间花在了重复的从左向右查找上(即查找“下一个”),那么,能不能记录左边已经遍历过的状态,然后在后面命中“目标值”的时候直接根据记录的信息得到答案呢?
第一步的分割仍然是基础,但我们把这一步分割用变量的状态来记录,每当命中一个大于right的值,我们就重新求解一个新区间;求解一个区间时,每当遍历到一个不大于right的值,我们就把他作为右端点,向左去查找符合题意的区间;倘若这个值介于 [left, right] 之间,任意向左(直到到达左边界)的区间均符合题意;倘若这个值小于 left,退化为与上一个(左边)介于 [left, right] 之间的数为右端点相同;这样,我们在前面的遍历就能确定并记录“上一个”,而无需重复查找。
还是给定上面的条件,nums = [2, 1, 1, 2, 4, 3],left = 2,right = 3,设 i 为当前指针,求解如下:
i = 0 (2):介于 [left, right] 之间,向左边界求数量:1;
i = 1 (1):小于left,退化为 i = 0 的情况,数量:1;
i = 2 (1):小于left,退化为 i = 0 的情况,数量:1;
i = 3 (2):介于 [left, right] 之间,向左边界求数量:4;
i = 4 (4):大于right,向后开启下一循环;
i = 5 (3):介于 [left, right] 之间,向左边界上一个左边界为(i = 4)求数量:1;
共 1 + 1 + 1 + 4 + 1 = 8 个;
三、C++代码
1、思路1:固定左端点,从左往右查找
#include <iostream>
#include <vector>
using namespace std;
//方法1:固定左端点,向后查找
class Solution1 {
public:
int getSubNum(vector<int>& nums, int l, int r, int left) {
int sub_res = 0;
for (int i = l; i < r; i++) {
if (nums[i] < left) {
//Searching behind until get legal num.
int j = i;
while (j < r && nums[j] < left) j++;
sub_res += (r - j);//if (j < r)
}
else
sub_res += (r - i);
}
return sub_res;
}
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
int l = 0;
int res = 0;
bool find_left = true;
int size = nums.size();
for (int i = 0; i < size; i++) {
if (find_left) {
if (nums[i] <= right) {
l = i;
find_left = false;
}
}
else {
if (nums[i] > right) {
find_left = true;
res += getSubNum(nums, l, i, left);
//cout << "db A: " << "le: " << l << " r:" << i
// << " addition:" << getSubNum(nums, l, i, left) << " res:" << res << endl;
}
}
}
if (!find_left) {
res += getSubNum(nums, l, size, left);
//cout << "db B: " << "le: " << l << " r(size):" << size
// << " addition:" << getSubNum(nums, l, size, left) << " res:" << res << endl;
}
return res;
}
};
int main()
{
Solution s;
vector<int> nums1 = { 2,1,4,3 };
vector<int> nums2 = { 2,9,2,5,6 };
vector<int> nums3 = { 2,2,1,1,2,1,2,4,2,3 };
vector<int> nums4 = { 73,55,36,5,55,14,9,7,72,52 };
cout << "Debug: " << endl
//<< "input: { 2,1,4,3 } 2 3 ; Excepted: 3 ; Actually: " << s.numSubarrayBoundedMax(nums1, 2, 3) << endl
//<< "input: { 2,9,2,5,6 } 2 8 ; Excepted: 7 ; Actually: " << s.numSubarrayBoundedMax(nums2, 2, 8) << endl
//<< "input: { 2,2,1,1,2,1,2,4,2,3 } 2 3 ; Excepted: 20 ; Actually: " << s.numSubarrayBoundedMax(nums3, 2, 3) << endl
<< "input: { 73,55,36,5,55,14,9,7,72,52 } 32 69 ; Excepted: 22 ; Actually: " << s.numSubarrayBoundedMax(nums4, 32, 69) << endl;
return 0;
}
2、思路2:固定右端点,从右往左查找
#include <iostream>
#include <vector>
using namespace std;
//方法2:固定右端点,向前查找
class Solution {
public:
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
int last2 = -1, last1 = -1;
int res = 0;
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] > right) { //status 2
last2 = i;
last1 = -1;
}
else if (nums[i] < left) { //status 0
if (last1 != -1) res += (last1 - last2);
}
else { //status 1
last1 = i;
res += (last1 - last2);
}
}
return res;
}
};
int main()
{
Solution s;
vector<int> nums1 = { 2,1,4,3 };
vector<int> nums2 = { 2,9,2,5,6 };
vector<int> nums3 = { 2,2,1,1,2,1,2,4,2,3 };
vector<int> nums4 = { 73,55,36,5,55,14,9,7,72,52 };
cout << "Debug: " << endl
//<< "input: { 2,1,4,3 } 2 3 ; Excepted: 3 ; Actually: " << s.numSubarrayBoundedMax(nums1, 2, 3) << endl
//<< "input: { 2,9,2,5,6 } 2 8 ; Excepted: 7 ; Actually: " << s.numSubarrayBoundedMax(nums2, 2, 8) << endl
//<< "input: { 2,2,1,1,2,1,2,4,2,3 } 2 3 ; Excepted: 20 ; Actually: " << s.numSubarrayBoundedMax(nums3, 2, 3) << endl
<< "input: { 73,55,36,5,55,14,9,7,72,52 } 32 69 ; Excepted: 22 ; Actually: " << s.numSubarrayBoundedMax(nums4, 32, 69) << endl;
return 0;
}
四、复杂度
方法2优化为:时间复杂度 O(N) ; 空间复杂度O(N)