图解LeetCode——1224. 最大相等频率(难度:困难)

一、题目

给你一个正整数数组 nums,请你帮忙从该数组中找出能满足下面要求的 最长 前缀,并返回该前缀的长度:

从前缀中 恰好删除一个 元素后,剩下每个数字的出现次数都相同。

如果删除这个元素后没有剩余元素存在,仍可认为每个数字都具有相同的出现次数(也就是 0 次)。

二、示例

2.1> 示例 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.2> 示例 2:

【输入】nums = [1,1,1,2,2,2,3,3,3,4,4,4,5]
【输出】13

提示:

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

三、解题思路

根据题目描述,我们需要找到一个最长的前缀,这个前缀要满足剩下的每个数字出现的次数都相同,那么我们其实可以根据这种要求来归类一下,大致有如下四种类型,是可以满足题目要求的。

首先,如果我们的前缀字符串都是不同的,并且出现的次数都是一次的话,那么我们无论是删除哪一个元素,剩余的元素的出现次数也都是1次,所以,这种类型我们可以通过计算每个元素的出现次数来做判断。那么,怎么能快速的确定每个元素的出现次数,并且可以快速判断出来出现次数为1的元素个数呢?这里面我们就需要两个变量了,一个用来存储元素它出现次数的关系——elementTimes,另一个用来存储出现了第N次元素的个数——timesGroup。那么当出现了第1次的元素个数等于当前遍历的个数的时候,就表示当前字符串中的元素,满足此类型。关于类型一,请参见下图:

其次,第二种类型的特征是,只有一个元素出现了N次,而其他的元素都只出现了N-1次。那么当我们将出现了N次的元素移除了一个之后,那么剩余的字符串中的所有元素,出现次数都是N-1次了。这里面我们通过引入一个参数——maxTimes,用于记录最大出现次数。每次遍历元素并计算其出现次数的时候,我们都可以采用Math的max(...)方法来对maxTimes进行同步更新。那么,我们就可以通过获取timesGroup[maxTimes]是否等于1并且计算timesGroup[maxTimes - 1] * (maxTimes - 1) + 1是否等于遍历的元素个数总和,来确定是否满足类型二的条件。关于类型二,请参见下图:

那么,第三种类型的特征就是,只有一个元素出现过1次,其他的元素,都出现过N次,那么这时候,当我们移除了仅仅出现过一次的元素时,自然剩下的所有元素,都是出现过N次的。这种情况,我们通过计算出现过N次元素的总和,即:timesGroup[maxTimes] * maxTimes + 1是否等于总遍历元素总和来判断是否满足类型三。关于类型三,请参见下图:

最后一种类型,就是一个元素出现了N次,并且只有这个元素出现了。那么,针对这种类型,无论去除哪个,结果都是满足条件的。关于类型四,其实在类型二的判断逻辑中,就已经包含了。所以,在代码实现中,就不用单独做处理了。关于类型四,请参见下图:

四、代码实现

4.1> 实现1:哈希Map + 最大次数

class Solution {
    public static int maxEqualFreq(int[] nums) {
        /**
         * 情况一:所有元素均出现1次——[1,2,3,4,5,6]
         * 情况二:多个出现N次和1个出现N+1次元素——[1,1,2,2,3,3,3]
         * 情况三:去除1个元素,就满足"最大出现次数"*"元素个数"——[2,2,2,1,1,1,3]、[1,1,1,1,1]、[1,1,1,1,2]
         */
        Map elementTimes = new HashMap(); // 记录元素出现的次数,即:key=[元素1] value=[出现2次]
        Map timesGroup = new HashMap(); // 记录某次数中存在的元素个数,即:key=[出现2次] value=[3个元素满足]
        int maxLength = 0, maxTimes = 0;
        for (int i = 0; i < nums.length; i++) {
            int num = elementTimes.getOrDefault(nums[i], 0);
            elementTimes.put(nums[i], num + 1);

            if (num > 0) timesGroup.put(num, timesGroup.get(num) - 1);
            timesGroup.put(num + 1, timesGroup.getOrDefault(num + 1, 0) + 1);
            maxTimes = Math.max(maxTimes, num + 1);

            if (timesGroup.get(1) == i + 1 || // 情况一
                    (timesGroup.get(maxTimes) == 1 && maxTimes + timesGroup.get(maxTimes - 1) * (maxTimes - 1) == i + 1) || // 情况二
                    (maxTimes == i + 1) || // 情况三-1
                    timesGroup.get(maxTimes) * maxTimes == i // 情况三-2
            ) maxLength = i + 1;
        }
        return maxLength;
    }
}

4.2> 实现2:哈希Map + 最大次数

class Solution {
    public static int maxEqualFreq(int[] nums) {
        /**
         * 情况一:所有元素均出现1次——[1,2,3,4,5,6]
         * 情况二:只有1个元素出现N次 以及 多个出现N次和1个出现N+1次元素——[1,1,1,1]、[1,1,2,2,3,3,3]
         * 情况三:去除1个元素,就满足"最大出现次数"*"元素个数"——[2,2,2,1,1,1,3]、[1,1,1,1,1]、[1,1,1,1,2]
         */
        int[] elementTimes = new int[100001]; // 记录元素出现的次数,即:index=[元素1] elementTimes[index]=[出现2次]
        int[] timesGroup = new int[100001]; // 记录某次数中存在的元素个数,即:index=[出现2次] timesGroup[index]=[3个元素满足]
        Arrays.fill(elementTimes, 0);
        Arrays.fill(timesGroup, 0);

        int maxLength = 0, maxTimes = 0;
        for (int i = 0; i < nums.length; i++) {
            int num = elementTimes[nums[i]];
            elementTimes[nums[i]] += 1;
            if (num > 0) timesGroup[num] += -1;
            timesGroup[num + 1] += 1;
            maxTimes = Math.max(maxTimes, num + 1);
            if (timesGroup[1] == i + 1 || // 情况一
                    (timesGroup[maxTimes] == 1 && maxTimes + timesGroup[maxTimes - 1] * (maxTimes - 1) == i + 1) || // 情况二
                    timesGroup[maxTimes] * maxTimes == i // 情况三
            ) maxLength = i + 1;
        }
        return maxLength;
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(o)/ ~ 「干货分享,每天更新」

你可能感兴趣的:(图解LeetCode——1224. 最大相等频率(难度:困难))