Count Subarrays With Fixed Bounds

Count Subarrays With Fixed Bounds

You are given an integer array nums and two integers minK and maxK .

A fixed-bound subarray of nums is a subarray that satisfies the following conditions:

  • The minimum value in the subarray is equal to minK .
  • The maximum value in the subarray is equal to maxK .

Return the number of fixed-bound subarrays.

subarray is a contiguous part of an array.

Example 1:

Input: nums = [1,3,5,2,7,5], minK = 1, maxK = 5
Output: 2
Explanation: The fixed-bound subarrays are [1,3,5] and [1,3,5,2].

Example 2:

Input: nums = [1,1,1,1], minK = 1, maxK = 1
Output: 10
Explanation: Every subarray of nums is a fixed-bound subarray. There are 10 possible subarrays.

Constraints:

2nums.length105
1nums[i],minK,maxK106

 

解题思路

  先说说比赛时的做法。先考虑暴力的做法,枚举右端点i,从i往左找到第一个小于minK或大于maxK的数,假设这个数的下标为j,那么很明显我们要求的子数组必定在[j+1,i]内。然后再从i往左求后缀最小值和后缀最大值,如果发现枚举到下标k时有第一次同时满足后缀最小值等于minK和后缀最大值等于maxK,那么此时在区间[j+1,k]内的下标都可以作为子数组的左端点,因此以i为右端点的满足条件的子数组有kj个。这种做法的时间复杂度是O(n2)

  接下来看看能不能对枚举的过程进行优化。首先是从右往左找到第一个超出minKmaxK范围内的数,这个可以开个变量来记录上一个不满足条件的数的下标,这样就不用每次都重新枚举。然后是找后缀最大值最小值,本质就是找到左边值为minKmaxK的最大下标,这两个下标的最小值就是上面所说的k,因此在往后枚举i的过程中可以开两个变量来分别记录minKmaxK的最大下标,这样就不用每次往左重新枚举了。时间复杂度就变成O(n)

  AC代码如下:

复制代码
 1 class Solution {
 2 public:
 3     long long countSubarrays(vector<int>& nums, int minK, int maxK) {
 4         long long ret = 0;
 5         for (int i = 0, t = -1, a = -1, b = -1; i < nums.size(); i++) {
 6             if (nums[i] < minK || nums[i] > maxK) {
 7                 t = i;
 8                 a = b = -1;
 9             }
10             else {
11                 if (nums[i] == minK) a = i;
12                 if (nums[i] == maxK) b = i;
13                 if (a != -1 && b != -1) ret += min(a, b) - t;
14             }
15         }
16         return ret;
17     }
18 };
复制代码

  下面说一下双指针的做法,当时写的时候没想到双指针怎么做。

  一样先把不在minKmaxK范围内的数找出来,这样就可以把数组分成若干段,答案也不可能跨过分界点,不然就不满足数组中最小值为minK和最大值为maxK,因此分别看每一段就可以了。

  枚举右端点i,在i的左边找到一个j,可以发现ji越远,范围就会越大,即更有可能包含更多的minKmaxK。因此j可以定义为最靠近i的位置,使得区间[j,i]内至少包含一个minKmaxK,这样j到分界点这些位置与右端点i都是可以构成满足要求的子数组,而j+1i这些位置都是不可以构成满足要求的子数组。

  当i往右走时j也只会往右走而不会往左走,因此可以用双指针。这是因为如果i往右走到ii对应的j是在j的左边(即j往左走),那么由于区间[j,i]内至少存在一个minKmaxK,因此区间[j,i]内也必定至少存在一个minKmaxK,这样[j,i]也是满足条件且比j更靠近i,这就证明指针j不会往左走。

  AC代码如下:

复制代码
 1 class Solution {
 2 public:
 3     long long countSubarrays(vector<int>& nums, int minK, int maxK) {
 4         long long ret = 0;
 5         int smin = 0, smax = 0;
 6         for (int i = 0, j = 0, last = 0; i < nums.size(); i++) {
 7             if (nums[i] < minK || nums[i] > maxK) {
 8                 last = j = i + 1;
 9                 smin = smax = 0;
10             }
11             else {
12                 if (nums[i] == minK) smin++;
13                 if (nums[i] == maxK) smax++;
14                 while (j < i) {
15                     if (nums[j] == minK) smin--;
16                     if (nums[j] == maxK) smax--;
17                     if (!smin || !smax) {
18                         if (nums[j] == minK) smin++;
19                         if (nums[j] == maxK) smax++;
20                         break;
21                     }
22                     j++;
23                 }
24                 if (smin && smax) ret += j - last + 1;
25             }
26         }
27         return ret;
28     }
29 };
复制代码

 

参考资料

  力扣第315场周赛:https://www.bilibili.com/video/BV1Yg411a7NM/

posted @   onlyblues  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示