题目描述
给出一个正整数数组 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
示例 3:
输入:nums = [1,1,1,2,2,2]
输出:5
示例 4:
输入:nums = [10,2,8,9,3,8,1,5,2,3,7,6]
输出:8
提示:
2 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
解题思考
一开始想的还是利用桶排来做这道题。
题目的关键是每个数字出现的频率,同时数字的数量已经确定了,那么筒式排序就可以直接用上了。创建一个大小为100000的数组cnt[100000]
,用于存放每个数字出现的次数。
其实,让题目难度增加的关键词只有一个,那就是前缀。题目要求找到最长前缀,那最直观的思路就是从前往后找,尽量遍历一遍数组,直接能够找到最长前缀。那么怎么遍历呢,这就是最难的地方。下面我来介绍一下思路:
- 其实,我们找到的最长前缀无非只有两种可能:
- 其他的数字都有相同频率,只有一个数字出现的次数比其他的多1;
- 其他的数字都有相同频率,只有一个数字只出现1次。
- 根据上面的思路,我们只需要找到两个条件,更新长度:
- 条件1: 当其他数字出现的次数都为
n
,而新读入的节点的数字出现的总次数为n+1
。 - 条件2: 当其他数字出现的次数都为
n
,而新读入的节点的数字第1
次出现。
- 条件1: 当其他数字出现的次数都为
- 只有当遇到上面两种情况,更新最长前缀的长度,其他时候不做任何操作。
主要思路出现了,那我们怎么判断其他数字出现的次数都为n
,而新读入的节点的次数为n+1
或者1
呢?
这个时候,我们可以再声明一个数组,保存频率出现的次数。例如nums = {1, 1, 1, 2, 2, 2, 2}
。我们每读入一个节点nums[n]
,cnt[nums[n]]++
,声明一个freq[100000]
,保存每个cnt
出现的次数,则freq[cnt[nums[n]]]++
。与此同时,因为我们要找到出现频率最多的那个数字,因此令maxn = max(cnt)
。
用上面的nums
举个例子:
- 当
n = 5
时 ,maxn = 3
,而cnt[1] = 3, cnt[2] = 3, freq[cnt[2]] = freq[cnt[1]] = 2
。freq[3] = 2
说明数量为3的数字有两个。至此,读入的总数字数量为freq[3] * 2 = 6
。 - 当
n = 6
时,maxn = 4
,开始进行判断。如果freq[4] = 1
(说明最大出现次数是第一次出现,符合上面条件1的后半部分),判断freq[maxn - 1] * (maxn - 1) = n
(出现maxn - 1次的数字的总数量 = 除当下数字之外的数字总数,即,其他数字频率一样。符合上面条件1的前半部分),那么1条件都满足,更新长度。 - 又假设
nums = {1, 1, 1, 2, 2, 2, 3}
,当n = 6
时,maxn = 3
,开始进行判断。此时freq[maxn] * maxn = n
,即除了当下的数,其他数出现的频率相同,而且当下的数出现的次数仅为1。因此,满足条件2,更新长度。
上面的思路的关键是freq, maxn
的正确应用,思路不是很好想,也基本符合Hard的难度。我试过用另一个思路去做这道题,但是运行时间很长,通过所有用例的运行总时长是这个思路的30倍,我就不放出来献丑了。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
static int maxEqualFreq(vector<int> &nums) {
int cnt[100000] = {0}, freq[100000] = {0};
int ans = 0, maxn = 0;
int i = 0;
while (i < nums.size()) {
maxn = max(maxn, ++cnt[nums[i]]);
freq[cnt[nums[i]]]++;
if (freq[maxn] == 1 && freq[maxn - 1] * (maxn - 1) == i) {
// 只有一个最大出现次数
ans = i;
} else if (freq[maxn] * maxn == i) {
// 如果数字出现的频率都一样
ans = i;
}
i++;
}
int result = maxn == 1 ? nums.size() : ans + 1;
return result;
}
};
int main() {
vector<int> s = {2,2,2,1,1,1,5,3,3,5};
int result;
Solution solution;
result = solution.maxEqualFreq(s);
cout << result;
}
总结
这个思路非常有意思,因而虽然难度为HARD,但是实际上代码非常简洁,同时时间复杂度为 O ( n ) O(n) O(n) ( n n n是数组长度)。