from collections import Counter, defaultdict
class Solution:
def maxEqualFreq(self, nums):
count = Counter()
freq = Counter()
res = 0
for i, num in enumerate(nums):
if count[num]:
freq[count[num]] -= 1
count[num] += 1
freq[count[num]] += 1
maxf = max(count.values())
n = i + 1
# 1. All numbers appear once
# 2. All but one number appear maxf, one appears once
# 3. All but one number appear maxf-1, one appears maxf
if maxf == 1 or \
freq[maxf] * maxf + freq[maxf-1] * (maxf-1) == n and freq[maxf] == 1 or \
freq[maxf] * maxf + 1 == n and freq[1] == 1:
res = n
return res
#include <unordered_map>
#include <algorithm>
using namespace std;
class Solution {
public:
int maxEqualFreq(vector<int>& nums) {
unordered_map<int, int> count, freq;
int res = 0;
for (int i = 0; i < nums.size(); ++i) {
int num = nums[i];
if (count[num])
freq[count[num]]--;
count[num]++;
freq[count[num]]++;
int maxf = 0;
for (auto &p : count)
maxf = max(maxf, p.second);
int n = i + 1;
if (maxf == 1 ||
freq[maxf] * maxf + freq[maxf - 1] * (maxf - 1) == n && freq[maxf] == 1 ||
freq[maxf] * maxf + 1 == n && freq[1] == 1) {
res = n;
}
}
return res;
}
};
import java.util.*;
class Solution {
public int maxEqualFreq(int[] nums) {
Map<Integer, Integer> count = new HashMap<>();
Map<Integer, Integer> freq = new HashMap<>();
int res = 0;
for (int i = 0; i < nums.length; ++i) {
int num = nums[i];
if (count.containsKey(num)) {
freq.put(count.get(num), freq.get(count.get(num)) - 1);
}
count.put(num, count.getOrDefault(num, 0) + 1);
freq.put(count.get(num), freq.getOrDefault(count.get(num), 0) + 1);
int maxf = 0;
for (int v : count.values())
maxf = Math.max(maxf, v);
int n = i + 1;
if (maxf == 1 ||
freq.getOrDefault(maxf, 0) * maxf + freq.getOrDefault(maxf - 1, 0) * (maxf - 1) == n && freq.getOrDefault(maxf, 0) == 1 ||
freq.getOrDefault(maxf, 0) * maxf + 1 == n && freq.getOrDefault(1, 0) == 1) {
res = n;
}
}
return res;
}
}
var maxEqualFreq = function(nums) {
let count = new Map();
let freq = new Map();
let res = 0;
for (let i = 0; i < nums.length; ++i) {
let num = nums[i];
if (count.has(num)) {
freq.set(count.get(num), (freq.get(count.get(num)) || 0) - 1);
}
count.set(num, (count.get(num) || 0) + 1);
freq.set(count.get(num), (freq.get(count.get(num)) || 0) + 1);
let maxf = 0;
for (let v of count.values()) {
maxf = Math.max(maxf, v);
}
let n = i + 1;
if (maxf === 1 ||
(freq.get(maxf) * maxf + (freq.get(maxf - 1) || 0) * (maxf - 1) === n && freq.get(maxf) === 1) ||
(freq.get(maxf) * maxf + 1 === n && freq.get(1) === 1)) {
res = n;
}
}
return res;
};
You are given an array of positive integers nums
. Your task is to find the length of the longest prefix of nums
such that, after removing at most one element from this prefix, every number in the prefix appears the same number of times.
Constraints:
nums.length
≤ 105nums[i]
≤ 105At first glance, you might consider checking every possible prefix and, for each, removing every possible element to check if the remaining numbers all have the same frequency. However, this brute-force approach is too slow for large arrays.
To optimize, let's observe what it means for a prefix to be "almost" equal-frequency. If we can remove one element and all remaining elements have the same count, one of a few scenarios must be true:
Let's break down the algorithm step by step:
count
: tracks how many times each number has appeared.freq
: tracks how many numbers have a given frequency.count
.freq
(if it had appeared before).maxf
.maxf == 1
.maxf
, and one has maxf-1
(and only one number with maxf
): freq[maxf] * maxf + freq[maxf-1] * (maxf-1) == n
and freq[maxf] == 1
.maxf
: freq[maxf] * maxf + 1 == n
and freq[1] == 1
.Using hash maps ensures all operations are efficient, even for large arrays.
Let's take nums = [2,2,1,1,5,3,3,5]
.
[2,2,1,1,5,3,3]
3*2 + 1*1 = 7
and freq[2]=3
, freq[1]=1
. Yes![2,2,1,1,5,3,3,5]
This step-by-step check ensures we only accept prefixes where, after at most one removal, all numbers have equal frequency.
The key insight is that only a few special cases allow a prefix to be made equal-frequency by removing at most one element. By maintaining running counts and frequencies in hash maps, we can check these cases efficiently as we process the array. This approach ensures a linear-time solution, making it suitable for large inputs and leveraging the properties of prefix subarrays and frequency distributions.