面试题 17.05. 字母与数字 | 2488. 统计中位数为 K 的子数组(前缀和+哈希表)

文章介绍了两种编程问题的解决方案。第一题涉及寻找数组中最长的子数组,使得字母和数字个数相同,通过将数字转为1,字母转为-1,然后求前缀和,使用哈希表找到和为0的子数组。第二题要求统计中位数为K的子数组数量,通过转换数组,求前缀和,再用哈希表记录中位数之前的前缀和出现次数,从而计算满足条件的子数组个数。
摘要由CSDN通过智能技术生成
面试题 17.05. 字母与数字

给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。

返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。

示例 1:

输入: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7","H","I","J","K","L","M"]

输出: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7"]

示例 2:

输入: ["A","A"]

输出: []

思路:转化+前缀和+哈希表

1、先将array中的数字元素转为1,字母元素转为-1,存到trans数组中;
2、因为要求array的一个最长子数组,包含的字母和数字个数相同,所以可以转为要求trans的一个最长子数组,它的元素之和为0;
3、为了快速求出子数组元素之和,求trans数组的前缀和,记为perSum数组,
如果一个子数组[left,right]元素之和为0,则preSum[right+1]-preSum[left]就应该等于0,
换句话说,preSum[right+1]等于preSum[left];
· 最终转为:从前缀和数组preSum中找到两个相同的数preSum[right]和preSum[left],计算
len(即right−left)的最大值。
利用哈希表保存preSum中元素preSum[i]第一次出现的位置,当它第二次出现时,就可以更新最长子数组的起点和长度了。
class Solution {
public:
    vector<string> findLongestSubarray(vector<string>& array) {
        int n=array.size();
        vector<int> trans(n);//将array中的数字元素转为1,字母元素转为-1
        for(int i=0;i<n;i++)
            if(array[i][0]>='0'&&array[i][0]<='9')
                trans[i]=1;
            else
                trans[i]=-1;

        vector<int> preSum(n+1,0);//求trans的前缀和数组
        for(int i=0;i<n;i++)
            preSum[i+1]=preSum[i]+trans[i];
        
        unordered_map<int,int> firOccur;//保存前缀和数组中元素第一次出现的位置
        int start=0,len=0;//最长子数组的起始位置和长度
        for(int i=0;i<=n;i++){
            if(!firOccur.count(preSum[i]))
                firOccur[preSum[i]]=i;
            else{
                int cur=i-firOccur[preSum[i]];
                if(cur>len)//更新最长子数组的起点和长度{
                    start=firOccur[preSum[i]];
                    len=cur;
                }
            }
        }
        vector<string> res(len);
        for(int i=0;i<len;i++){
            res[i]=array[i+start];
        }
        return res;
    }
};
2488. 统计中位数为 K 的子数组

给你一个长度为 n 的数组 nums ,该数组由从 1 到 n 的 不同 整数组成。另给你一个正整数 k 。

统计并返回 nums 中的 中位数 等于 k 的非空子数组的数目。

注意:

  • 数组的中位数是按 递增 顺序排列后位于 中间 的那个元素,如果数组长度为偶数,则中位数是位于中间靠 的那个元素。

  • 例如,[2,3,1,4] 的中位数是 2 ,[8,4,3,5,1] 的中位数是 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 的子数组。

思路:转化+前缀和+哈希表

  1. 转换nums数组,大于k的记为1,小于k的记为-1,等于k的记为0;

  1. 求出nums数组的前缀和数组s;

  1. 记录k之前s[i]的出现次数

  1. 统计结果。

  • 如果k是子数组[i..j]的中位数,则[i..k)的元素个数应该等于(k,j]的元素个数,或者比(k,j]的元素个数小1

  • 由于nums[i]大于k的记为1,小于k的记为-1,并且得出了前缀和数组s

  • 所以s[right]-s[left]==0或s[right]-s[left]==1,表示k为数组[left..right)的中位数

class Solution {
public:
    int countSubarrays(vector<int>& nums, int k) {
        int n=nums.size();
        int index=0;
        for(int i=0;i<n;i++) //转换nums数组,大于k的记为1,小于k的记为-1,等于k的记为0
        {
            if(nums[i]>k)
                nums[i]=1;
            else if(nums[i]<k)
                nums[i]=-1;
            else {
                nums[i]=0;
                index=i;//记录k的下标
            }
        }
        vector<int> s(n+1);//前缀和
        for(int i=1;i<=n;i++)
        {
            s[i]=s[i-1]+nums[i-1];
        }
        unordered_map<int,int> memo;
        for(int i=0;i<index+1;i++)//记录k之前s[i]的出现次数
        {
            memo[s[i]]++;
        }
        int res=0;
        //如果k是子数组[i..j]的中位数,则[i..k)的元素个数应该等于(k,j]的元素个数,或者比(k,j]的元素个数小1
        //由于nums[i]大于k的记为1,小于k的记为-1,并且得出了前缀和数组s
        //所以s[right]-s[left]==0或s[right]-s[left]==1,表示k为数组[left..right)的中位数
        for(int i=index+1;i<=n;i++)
        {
            if(memo.count(s[i]))//s[right]-s[left]==0
                res+=memo[s[i]];
            if(memo.count(s[i]-1))//s[right]-s[left]==1
                res+=memo[s[i]-1];
        }
        return res;
    }   
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值