链接:
https://leetcode.cn/problems/count-subarrays-with-median-k/
题意:
给一个数组,找出排序后中位数为k的非空子数组的数量,数组长度为n,内含1到n不重复整数
当子数组长度为偶数时,k要在偏左一侧
解:
将大于k的视为1,小于k的视为-1,前缀sz[i+1]可以表示从[0,i]
这个子数组中,k于中位的偏移值(k不一定在其中)
对于任意一段所需子数组,首先要满足k被包含在其中
可知,当k为中位数且k在该段中时,该段中大于k的数字和小于k的数字数量相等,偏移值为0;
同理,当k为中位偏左且k在该段中时,偏移值需要为1,该段中大于k的数字比小于k的数字数量多一个。
所以当该段偏移值为0或1,且k在该段中时,一定是所需子数组
遍历数组,小于k的存进map,要初始化map[0]=1,因为我的sz[i]表示包含i在内的前缀数组偏移值,sz[0]就含有nums[0]的偏移值,要考虑起点在0号元素之前的情况(错了两发),等于k的偏移值也不用存,因为子数组起点必须在k之前(错了一发)。
大于等于k的每个偏移值sz[i+1]
都为答案提供map[ sz[i+1] ]+map[ sz[i+1]-1 ]
的值
[L,R]偏移值为sz[R+1]-sz[L]
,因为sz数组是对下标+1存储的,所以代码太丑陋了
实际代码:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=1E5+3;
int sz[N],mao;
int solve(vector<int>& nums, int k)
{
map<int,int>pd;//配对组
int lg=nums.size(),ans=0;
//前缀+绝对处理
for(int i=0;i<lg;i++)
{
if(nums[i]<k) sz[i+1]=sz[i]-1;
if(nums[i]==k)
{
sz[i+1]=sz[i];
mao=i;
}
if(nums[i]>k) sz[i+1]=sz[i]+1;
//cout<<sz[i+1]<<endl;
}
pd[0]=1;//最左端为起点
for(int i=0;i<lg;i++)
{
if(i<mao) pd[ sz[i+1] ]++;//累计 起点
else
{
ans=ans+pd[ sz[i+1] ]+pd[ sz[i+1]-1 ];
}
//cout<<"temp_ans:"<<ans<<endl;
}
//返回答案
return ans;
}
int main()
{
int n,k;cin>>n;
vector<int> nums;
for(int i=1;i<=n;i++)
{
int temp;cin>>temp;
nums.push_back(temp);
}
cin>>k;
int ans=solve(nums,k);
cout<<"ans="<<ans<<endl;
}
限制:
n == nums.length
1 <= n <= 105
1 <= nums[i], k <= n
nums
中的整数互不相同