[LeetCode 1224] Maximum Equal Frequency

Given an array nums of positive integers, return the longest possible length of an array prefix of nums, such that it is possible to remove exactly one element from this prefix so that every number that has appeared in it will have the same number of occurrences.

If after removing one element there are no remaining elements, it's still considered that every appeared number has the same number of ocurrences (0).

Example 1:

Input: nums = [2,2,1,1,5,3,3,5]
Output: 7
Explanation: For the subarray [2,2,1,1,5,3,3] of length 7, if we remove nums[4]=5, we will get [2,2,1,1,3,3], so that each number will appear exactly twice.

Example 2:

Input: nums = [1,1,1,2,2,2,3,3,3,4,4,4,5]
Output: 13

Example 3:

Input: nums = [1,1,1,2,2,2]
Output: 5

Example 4:

Input: nums = [10,2,8,9,3,8,1,5,2,3,7,6]
Output: 8

Constraints:

  • 2 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

Your solution during contest. 

Runtime is O(N), space is O(N) as well. But HashMap and HashSet are not as fast as simple array mapping.

class Solution {
    public int maxEqualFreq(int[] nums) {
        int res = nums.length;
        //m1: number -> its frequency
        Map<Integer, Integer> m1 = new HashMap<>();
        //m2: frequency -> all numbers that have this frequency.
        Map<Integer, Set<Integer>> m2 = new HashMap<>();
        
        //init m1
        for(int i = 0; i < nums.length; i++) {
            m1.put(nums[i], m1.getOrDefault(nums[i], 0) + 1);
        }
        //init m2
        for(int k : m1.keySet()) {
            int v = m1.get(k);
            if(!m2.containsKey(m1.get(k))) {
                m2.put(v, new HashSet<>());
            }
            m2.get(v).add(k);
        }
        //starting from the longest possible prefix, check if it meets the requirement. If it does, return the current prefix length as result;
        //If it does not, remove the current number from both maps and update the two maps accordingly.
        //Only when we have 1 or 2 unique frequencies in the current prefix window, there is a possible valid solution.
        //2 frequencies: a single number with 1 more occurence that all the rest numbers or a single number with 1 occurence;
        //1 frequency: all numbers with 1 occurence or there is only one number
        for(; res > 1; res--) {
            if(m2.size() == 2) {
                int maxK = 0, minK = Integer.MAX_VALUE;
                for(int k : m2.keySet()) {
                    maxK = Math.max(maxK, k);
                    minK = Math.min(minK, k);
                }
                if(maxK - minK == 1 && m2.get(maxK).size() == 1 || minK == 1 && m2.get(minK).size() == 1) {
                    break;
                }
            }
            else if(m2.size() == 1) {
                int onlyK = 0;
                for(int k : m2.keySet()) {
                    onlyK = k;
                }
                if(onlyK == 1 || m2.get(onlyK).size() == 1) {
                    break;
                }
            }
            int oldFreq = m1.get(nums[res - 1]);
            int newFreq = oldFreq - 1;
            m1.put(nums[res - 1], newFreq);
            m2.get(oldFreq).remove(nums[res - 1]);
            if(m2.get(oldFreq).size() == 0) {
                m2.remove(oldFreq);
            }
            if(newFreq > 0) {
                if(!m2.containsKey(newFreq)) {
                    m2.put(newFreq, new HashSet<>());
                }
                m2.get(newFreq).add(nums[res - 1]);                
            }
        }
        return res;
    }
}

A much better and cleaner solution.

cnt[i]: the occurence of number i;

freq[j]: the total count of different numbers that share the same occurence of j. 

maxF: the max frequency of all numbers that have been processed.  Only the current visiting number may change maxF. 

Same with the above solution, there are 4 cases we should consider.

1. All numbers appear exactly once.  maxF == 1

2. There is only 1 number appear in the current running window. maxF == i + 1

3. All numbers appear max_F times, except one number appears once. maxF * freq[maxF] + 1 == i + 1

4. All numbers appear max_F - 1 times, except one number appears maxF times. (maxF - 1) * freq[maxF - 1] + maxF == i + 1

Case 1 is actually a special case of case 4 where freq[maxF - 1] = 0. 

The trick here is to check if the total count of all numbers we've processed is the same length with the current running window. The reason that this works is because we use the max frequency to do the calculation. The only different frequencies we can have is at most 2. If more than 2, using the max frequency in our calculation always generates a greater length than the current window.

class Solution {
    public int maxEqualFreq(int[] A) {
        int[] cnt = new int[100001], freq = new int[100001];
        int maxF = 0, res = 0;
        for(int i = 0; i < A.length; i++){
            int num = A[i];
            cnt[num]++;
            freq[cnt[num]-1]--;
            freq[cnt[num]]++;
            maxF = Math.max(maxF,cnt[num]);
            if(maxF == 1 || maxF * freq[maxF] + 1 == i + 1 || (maxF - 1) * freq[maxF - 1] + maxF == i + 1) {
                res = i + 1;
            }                
        }
        return res;
    }
}

猜你喜欢

转载自www.cnblogs.com/lz87/p/11669371.html
今日推荐