Leetcode.2488 统计中位数为 K 的子数组 Rating :1999
给你一个长度为 n 的数组 nums
,该数组由从 1
到 n
的 不同 整数组成。另给你一个正整数 k
。
统计并返回 nums
中的 中位数 等于 k
的非空子数组的数目。
注意:
[2,3,1,4]
的中位数是 2
,[8,4,3,5,1]
的中位数是 4
。输入:nums = [3,2,1,4,5], k = 4
输出:3
解释:中位数等于 4 的子数组有:[4]、[4,5] 和 [1,4,5] 。
输入:nums = [2,3,1], k = 3
输出:1
解释:[3] 是唯一一个中位数等于 3 的子数组。
解法:前缀和 + 哈希表
等价转换:
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& 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 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}) , 0for i in range(idx - 1, -1, -1):sum = sum + 1 if nums[i] > k else sum - 1cnt[sum] += 1ans = cnt[0] + cnt[1]sum = 0for i in range(idx + 1,n):sum = sum + 1 if nums[i] > k else sum - 1ans += cnt[-1*sum]ans += cnt[1-sum]return ans