给你一个整数数组 nums
和两个整数 minK
以及 maxK
。
nums
的定界子数组是满足下述条件的一个子数组:
- 子数组中的 最小值 等于
minK
。 - 子数组中的 最大值 等于
maxK
。
返回定界子数组的数目。
子数组是数组中的一个连续部分。
示例 1:
输入:nums = [1,3,5,2,7,5], minK = 1, maxK = 5 输出:2 解释:定界子数组是 [1,3,5] 和 [1,3,5,2] 。
示例 2:
输入:nums = [1,1,1,1], minK = 1, maxK = 1 输出:10 解释:nums 的每个子数组都是一个定界子数组。共有 10 个子数组。
提示:
2 <= nums.length <= 10^5
1 <= nums[i], minK, maxK <= 10^6
提示 1
Can you solve the problem if all the numbers in the array were between minK and maxK inclusive?
提示 2
Think of the inclusion-exclusion principle.
提示 3
Divide the array into multiple subarrays such that each number in each subarray is between minK and maxK inclusive, solve the previous problem for each subarray, and sum all the answers.
解法:枚举子数组的一个端点,分析另一个端点的可行的范围
定界子数组满足性质:
- 子数组不能包含越界的数字 (nums[j] > maxK 或 nums[j] < minK) ;
- 子数组必须同时包含 maxK 和 minK。
根据上述条件,我们从左到右遍历数组,统计以 j 为右端点的定界子数组数量:
维护左侧上一个越界数字的位置 prev,表示左端点不能等于或越过 prev;
同时,分别维护 maxK 和 minK 在左侧上一次出现的位置 maxl 和 minl,表示左端点必须在 min(maxl, minl) 及其左侧,否则子数组中会缺少 maxK 或 minK;
因此,以 j 为右边界的子数组数量(如果存在)= min(maxl, minl) − prev
class Solution {
public long countSubarrays(int[] nums, int minK, int maxK) {
int n = nums.length;
long ans = 0;
// prev表示上一个越界的位置下标
int prev = -1;
// minl表示上一个minK出现的位置下标
int minl = -1;
// minl表示上一个maxK出现的位置下标
int maxl = -1;
// nums[i..j]是定界子数组,遍历右端点j,找到符合要求的i的范围
for (int j = 0; j < n; j++) {
// i在范围(prev, min(minl, maxl)]内
if(nums[j] == minK) {
minl = j;
}
if (nums[j] == maxK) {
maxl = j;
}
if (nums[j] < minK || nums[j] > maxK) {
prev = j;
}
// Math.min(maxl, minl) - prev < 0 说明,
// 此时子数组还不包括minK 或 maxK,不是一个定界子数组
ans += Math.max(0, Math.min(maxl, minl) - prev);
}
return ans;
}
}
复杂度分析
- 时间复杂度:O(n),n 是 数组 nums 的长度。
- 空间复杂度:O(1)。