题目
We are given an array A of positive integers, and two positive integers L and R (L <= R).
Return the number of (contiguous, non-empty) subarrays such that the value of the maximum array element in that subarray is at least L and at most R.
- Example :
Input:
A = [2, 1, 4, 3]
L = 2
R = 3
Output: 3
Explanation: There are three subarrays that meet the requirements: [2], [2, 1], [3]. - Note:
L, R and A[i] will be an integer in the range [0, 10^9].
The length of A will be in the range of [1, 50000].
#分析题目
- 本题给了我们一个数组,又给了我们两个数字L和R,表示一个区间范围,让我们求有多少个这样的子数组,使得其最大值在[L, R]区间的范围内。
- 求子数组的问题,第一种暴力求解就是遍历所有的子数组,然后维护一个当前的最大值,只要这个最大值在[L, R]区间的范围内,结果res自增1即可.我们可以在这基础上略作优化.首先,如果当前数字大于R了,那么其实后面就不用再遍历了,不管当前这个数字是不是最大值,它都已经大于R了,那么最大值可能会更大,所以没有必要再继续遍历下去了。同样的剪枝也要加在内层循环中加,当curMax大于R时,直接break掉内层循环即可.(参见解法一)
- 优化到线性的时间复杂度内完成。我们来看数组[2, 1, 4, 3],当遍历到2时,只有一个子数组[2],遍历到1时,有三个子数组,[2], [1], [2,1]。当遍历到4时,有六个子数组,[2], [1], [4], [2,1], [1,4], [2,1,4]。当遍历到3时,有十个子数组。则长度为n的数组共有n(n+1)/2个子数组,刚好是等差数列的求和公式。我们使用left和right来分别标记子数组的左右边界,使得其最大值在范围[L,R]内。那么当遍历到的数字大于等于L时,right赋值为当前位置i,那么每次res加上right - left,随着right的不停自增1,每次加上的right - left,实际上也是一个等差数列.当A[i]大于R的时候,left = i,那么此时A[i]肯定也大于等于L,于是rihgt=i,那么right - left为0,增量重置为0的操作.(参见解法二)
#解法一 - 时间复杂度O(n2 )
- 空间复杂度O(1)
#代码
class Solution {
public int numSubarrayBoundedMax(int[] A, int L, int R) {
int res = 0, n = A.length;
for (int i = 0; i < n; ++i) {
if (A[i] > R) continue;
int curMax = Integer.MIN_VALUE;
for (int j = i; j < n; ++j) {
curMax = Math.max(curMax, A[j]);
if (curMax > R) break;
if (curMax >= L) ++res;
}
}
return res;
}
}
#解法二
- 时间复杂度O(n)
- 空间复杂度O(1)
#代码
class Solution {
public int numSubarrayBoundedMax(int[] A, int L, int R) {
int res = 0, left = -1, right = -1;
for(int i = 0; i < A.length; i++) {
if(A[i] >= L) {
right = i;
}
if(A[i] > R) {
left = i;
}
res+=right-left;
}
return res;
}
}