题目链接
Leetcode.2488 统计中位数为 K 的子数组
rating :1999
题目描述
给你一个长度为 n n n 的数组 n u m s nums nums,该数组由从 1 1 1 到 n n n 的 不同 整数组成。另给你一个正整数 k k k。
统计并返回 n u m s nums nums 中的 中位数 等于 k k k 的 非空子数组的数目。
注意:
- 数组的中位数是按 递增 顺序排列后位于 中间 的那个元素,如果数组长度为偶数,则中位数是位于中间靠 左 的那个元素。
例如, [ 2 , 3 , 1 , 4 ] [2,3,1,4] [2,3,1,4] 的中位数是 2 2 2, [ 8 , 4 , 3 , 5 , 1 ] [8,4,3,5,1] [8,4,3,5,1] 的中位数是 4 4 4。 - 子数组是数组中的一个连续部分。
示例 1:
输入:nums = [3,2,1,4,5], k = 4
输出:3
解释:中位数等于 4 的子数组有:[4]、[4,5] 和 [1,4,5] 。
示例 2:
输入:nums = [2,3,1], k = 3
输出:1
解释:[3] 是唯一一个中位数等于 3 的子数组。
提示:
- n = n u m s . l e n g t h n = nums.length n=nums.length
- 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105
- 1 ≤ n u m s [ i ] , k ≤ n 1 \leq nums[i], k \leq n 1≤nums[i],k≤n
- n u m s nums nums 中的整数互不相同
解法:前缀和 + 哈希表
等价转换:
nums[i] > k
,把nums[i]
看作1
。nums[i] < k
,把nums[i]
看作-1
。nums[i] == k
,把nums[i]
看作0
。
就将求 中位数为k
的子数组 转换为求 包括nums[i]==0
这一项的且子数组和为 1
或者 0
的子数组。
所以我们最终的目的就是 求子数组和为 0
或者 1
的子数组个数(其中必须包含 nums[i]==0
这一项)。
我们用 idx
(nums[idx] == k
)把原数组分为两段 [0 , idx - 1]
,[idx + 1 , n - 1]
。
我们用一个哈希表 cnt
记录子数组和 sum
的出现次数,cnt[0] = 1
代表 nums[idx]
本身也是一个答案。
- 对于
[0 , idx - 1]
,我们从idx - 1
遍历到0
(因为要求的子数组必须包含nums[idx]
),更新子数组和sum
的出现次数。由于只考虑了左边的一段,所以合法的答案有sum == 0
和sum == 1
的子数组个数,即cnt[0] + cnt[1]
。我们先用ans = cnt[0] + cnt[1]
。 - 对于
[idx + 1 , n - 1]
,我们从idx + 1
遍历到n - 1
(因为要求的子数组必须包含nums[idx]
),更新子数组和sum
的出现次数。这时我们需要考虑左右两边,左右的子数组的和分别为leftSum
和rightSum
,leftSum + rightSum == 0 or 1
。此时我们枚举的是rightSum
,所以只需要让ans
加上cnt[1 - rightSum]
和cnt[-rightSum]
h即可。
时间复杂度: O ( n ) O(n) O(n)
C++代码:
class Solution {
public:
int countSubarrays(vector<int>& nums, int k) {
int n = nums.size();
int idx = -1;
for(int i = 0;i < n;i++){
if(nums[i] == k){
idx = i;
break;
}
}
unordered_map<int,int> cnt;
cnt[0] = 1;
int sum = 0;
for(int i = idx - 1;i >= 0;i--){
if(nums[i] > k) sum++;
else sum--;
cnt[sum]++;
}
int ans = cnt[0] + cnt[1];
sum = 0;
for(int i = idx + 1;i < n;i++){
if(nums[i] > k) sum++;
else sum--;
ans += cnt[-1*sum];
ans += cnt[1 - sum];
}
return ans;
}
};
Python代码:
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
idx = nums.index(k);
n = len(nums)
cnt , sum = Counter({0:1}) , 0
for i in range(idx - 1, -1, -1):
sum = sum + 1 if nums[i] > k else sum - 1
cnt[sum] += 1
ans = cnt[0] + cnt[1]
sum = 0
for i in range(idx + 1,n):
sum = sum + 1 if nums[i] > k else sum - 1
ans += cnt[-1*sum]
ans += cnt[1-sum]
return ans