携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情
题目描述
给你一个正整数数组 nums,请你帮忙从该数组中找出能满足下面要求的 最长 前缀,并返回该前缀的长度:
从前缀中 恰好删除一个 元素后,剩下每个数字的出现次数都相同。 如果删除这个元素后没有剩余元素存在,仍可认为每个数字都具有相同的出现次数(也就是 0 次)。
示例 1:
输入:nums = [2,2,1,1,5,3,3,5]
输出:7
解释:对于长度为 7 的子数组 [2,2,1,1,5,3,3],如果我们从中删去 nums[4] = 5,就可以得到 [2,2,1,1,3,3],里面每个数字都出现了两次。
示例 2:
输入:nums = [1,1,1,2,2,2,3,3,3,4,4,4,5]
输出:13
提示:
- 2 <= nums.length <= 105
- 1 <= nums[i] <= 105
思路
本题的难点在于,如何快速判断当前这个前缀是否符合条件。
我们定义一个Map:cnt来存储数字key出现的次数,定义一个Map:freq来存储出现次数为key的数字有几个,maxFreq记录出现最多的次数。
有如下4种情况是符合条件的:
- maxFreq = 1,此时每个数字都是出现1次,任意删除1个,剩下的出现次数也都是1
- freq 只包含1个key
- freq 只包含2个key,maxFreq 和 maxFreq -1,且出现maxFreq的数字个数为1,那么只要移除这个数字1次,剩下所有数字出现次数为maxFreq -1
- freq 只包含2个key,maxFreq 和 1,且出现1次的数字个数为1,那么只要移除这个只出现1次的数字,剩下所有数字出现次数都是maxFreq
Java版本代码
class Solution {
public int maxEqualFreq(int[] nums) {
// 数字key出现的次数
Map<Integer, Integer> cnt = new HashMap<>();
// 出现次数为key的数字有几个
Map<Integer, Integer> freq = new HashMap<>();
int maxFreq = 0;
int ans = 1;
for (int i = 0; i < nums.length; i++) {
// 如果这个数字出现过,那么需要维护freq,因为这个数字出现的次数+1,所以原始出现次数对应的数字个数要-1
if (cnt.containsKey(nums[i])) {
int times = cnt.get(nums[i]);
if (freq.get(times) == 1) {
freq.remove(times);
} else {
freq.put(times, freq.get(times) - 1);
}
}
cnt.put(nums[i], cnt.getOrDefault(nums[i], 0) + 1);
int times = cnt.get(nums[i]);
freq.put(times, freq.getOrDefault(times, 0) + 1);
maxFreq = Integer.max(maxFreq, times);
// 4种情况是符合条件的:
// maxFreq = 1,此时每个数字都是出现1次,任意删除1个,剩下的出现次数也都是1
// freq 只包含1个key
// freq 只包含2个key,maxFreq 和 maxFreq -1,且出现maxFreq的数字个数为1,那么只要移除这个数字1次,剩下所有数字出现次数为maxFreq -1
// freq 只包含2个key,maxFreq 和 1,且出现1次的数字个数为1,那么只要移除这个只出现1次的数字,剩下所有数字出现次数都是maxFreq
// 判断的时候,freq肯定包含key为maxFreq的键值对,这个不需要再次判断
boolean flag = maxFreq == 1 || (freq.size() == 1 && freq.get(maxFreq) == 1) ||
(freq.size() == 2 && freq.containsKey(maxFreq-1) && freq.get(maxFreq) == 1) ||
(freq.size() == 2 && freq.containsKey(1) && freq.get(1) == 1);
if (flag) {
ans = i + 1;
}
}
return ans;
}
}