面试题 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 的子数组。
思路:转化+前缀和+哈希表
转换nums数组,大于k的记为1,小于k的记为-1,等于k的记为0;
求出nums数组的前缀和数组s;
记录k之前s[i]的出现次数
统计结果。
如果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;
}
};