开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
给你一个整数数组 nums 和两个整数:left 及 right 。找出 nums 中连续、非空且其中最大元素在范围 [left, right] 内的子数组,并返回满足条件的子数组的个数。
生成的测试用例保证结果符合 32-bit 整数范围。
示例 1:
输入: nums = [2,1,4,3], left = 2, right = 3
输出: 3
解释: 满足条件的三个子数组:[2], [2, 1], [3]
示例 2:
输入: nums = [2,9,2,5,6], left = 2, right = 8
输出: 7
提示:
1 <= nums.length <= 10^50 <= nums[i] <= 10^90 <= left <= right <= 10^9
思路
我们把数组中的值做一个映射,小于left的值映射为0,大于等于left且小于等于right映射为1,大于right映射为2。原始数组就变成了由0、1、2组成的数组,原始题目可以转换成求不包含2但至少包含一个1的子数组数量。
至少包含一个1的子数组数量,可以先求出由0和1组成的子数组数量,再减去由0组成的子数组数量。设s[i]...s[j](i <= j)都小于等于right。s[m]...s[n](i <= m <= n <= j)都小于left。用f(i, j)表示s[i]...s[j]的所有子数组数量,f1(i, j)表示满足不包含2但至少包含一个1的子数组数量。
f(i, j) = 1 + 2 + 3 + ... + (j - i + 1) = (j - i + 1) * (j - i + 1 + 1) / 2
f1(i, j) = f(i, j) - f(m, n) = (j - i + 1) * (j - i + 2) / 2 - (n - m + 1) * (n - m + 2) / 2 。
我们遍历数组,记下相应的下标位置,按照公式求解即可。
解题
/**
* @param {number[]} nums
* @param {number} left
* @param {number} right
* @return {number}
*/
var numSubarrayBoundedMax = function (nums, left, right) {
let res = 0;
nums.push(right + 1);
const n = nums.length;
let i = 0;
let start = 0;
let end = 0;
let x = 0;
let y = 0;
const count = (n) => ((1 + n) * n) / 2;
while (i < n) {
if (nums[i] <= right) {
start = i;
while (nums[i] <= right) {
if (nums[i] < left) {
x = i;
while (nums[i] < left) {
i++;
}
y = i;
res -= count(y - x);
} else {
i++;
}
}
end = i;
res += count(end - start);
} else {
i++;
}
}
return res;
};