2023-03-16力扣每日一题

链接:

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 中的整数互不相同

猜你喜欢

转载自blog.csdn.net/Fei_WuYan/article/details/129600732