3020.子集中元素的最大数量

目标

给你一个 正整数 数组 nums 。

你需要从数组中选出一个满足下述条件的子集:

  • 你可以将选中的元素放置在一个下标从 0 开始的数组中,并使其遵循以下模式:[x, x^2, x^4, ..., x^k/2, x^k, x^k/2, ..., x^4, x^2, x](注意,k 可以是任何 非负 的 2 的幂)。例如,[2, 4, 16, 4, 2] 和 [3, 9, 3] 都符合这一模式,而 [2, 4, 8, 4, 2] 则不符合。

返回满足这些条件的子集中,元素数量的 最大值 。

示例 1:

输入:nums = [5,4,1,2,2]
输出:3
解释:选择子集 {4,2,2} ,将其放在数组 [2,4,2] 中,它遵循该模式,且 22 == 4 。因此答案是 3 。

示例 2:

输入:nums = [1,3,2,4]
输出:1
解释:选择子集 {1},将其放在数组 [1] 中,它遵循该模式。因此答案是 1 。注意我们也可以选择子集 {2} 、{4} 或 {3} ,可能存在多个子集都能得到相同的答案。

说明:

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

思路

将正整数数组 nums 的子序列按照模式放入一个下标从 0 开始的数组中,模式为 [x x^2 x^4 x^8 ... x^k/2 x^k x^k/2 ... x^8 x^4 x^2 x]k 可以是任何非负的 2 的幂,即 1、2、4、8、16……。求满足条件的子序列的最大长度。

对数组中的元素计数,如果 x 出现次数大于等于 2 则开始倍增,将长度加 2,判断 x^2 是否出现,如果出现次数大于 1,则判断 (x^2)^2 是否出现,以此类推。循环结束时判断数字的出现次数,如果为 0 需要将前面的元素作为中间元素,之前长度加了 2 这里需要减 1,如果为 1 正好作为中间元素将长度加 1

代码


/**
 * @date 2026-06-29 17:33
 */
public class MaximumLength3020 {

    public int maximumLength(int[] nums) {
        Map<Integer, Integer> cnt = new HashMap<>();
        for (int num : nums) {
            cnt.merge(num, 1, Integer::sum);
        }
        int res = 1;
        if (cnt.get(1) != null) {
            res = Math.max(res, cnt.get(1) - (1 - cnt.get(1) % 2));
            cnt.remove(1);
        }
        for (Integer num : cnt.keySet()) {
            int l = 0;
            while (cnt.getOrDefault(num, 0) > 1) {
                l += 2;
                num *= num;
            }
            res = Math.max(res, l + (cnt.get(num) == null ? -1 : 1));
        }
        return res;
    }

}

性能

3737.统计主要元素子数组数目I

目标

给你一个整数数组 nums 和一个整数 target。

返回数组 nums 中满足 target 是 主要元素 的 子数组 的数目。

一个子数组的 主要元素 是指该元素在该子数组中出现的次数 严格大于 其长度的 一半 。

子数组 是数组中的一段连续且 非空 的元素序列。

示例 1:

输入: nums = [1,2,2,3], target = 2
输出: 5
解释:
以 target = 2 为主要元素的子数组有:
nums[1..1] = [2]
nums[2..2] = [2]
nums[1..2] = [2,2]
nums[0..2] = [1,2,2]
nums[1..3] = [2,2,3]
因此共有 5 个这样的子数组。

示例 2:

输入: nums = [1,1,1,1], target = 1
输出: 10
解释:
所有 10 个子数组都以 1 为主要元素。

示例 3:

输入: nums = [1,2,3], target = 4
输出: 0
解释:
target = 4 完全没有出现在 nums 中。因此,不可能有任何以 4 为主要元素的子数组。故答案为 0。

说明:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 10^9
  • 1 <= target <= 10^9

思路

定义子数组的 主要元素 为出现次数 严格大于 子数组长度一半 的元素。有一个数组 nums,找出以 target 为主要元素的子数组个数。

使用前缀和记录 target 的出现次数,保留循环子数组,判断 target 是否是它的主要元素即可。

代码


/**
 * @date 2026-06-25 9:12
 */
public class CountMajoritySubarrays3737 {

    public int countMajoritySubarrays(int[] nums, int target) {
        int n = nums.length;
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] + (nums[i] == target ? 1 : 0);
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                int l = j - i + 1;
                if (prefix[j + 1] - prefix[i] > l / 2) {
                    res++;
                }
            }
        }
        return res;
    }

}

性能

3751.范围内总波动值I

目标

给你两个整数 num1 和 num2,表示一个 闭 区间 [num1, num2]。

一个数字的 波动值 定义为该数字中 峰 和 谷 的总数:

  • 如果一个数位 严格大于 其两个相邻数位,则该数位为 峰。
  • 如果一个数位 严格小于 其两个相邻数位,则该数位为 谷。
  • 数字的第一个和最后一个数位 不能 是峰或谷。
  • 任何少于 3 位的数字,其波动值均为 0。

返回范围 [num1, num2] 内所有数字的波动值之和。

示例 1:

输入: num1 = 120, num2 = 130
输出: 3
解释:
在范围 [120, 130] 内:
120:中间数位 2 是峰,波动值 = 1。
121:中间数位 2 是峰,波动值 = 1。
130:中间数位 3 是峰,波动值 = 1。
范围内所有其他数字的波动值均为 0。
因此,总波动值为 1 + 1 + 1 = 3。

示例 2:

输入: num1 = 198, num2 = 202
输出: 3
解释:
在范围 [198, 202] 内:
198:中间数位 9 是峰,波动值 = 1。
201:中间数位 0 是谷,波动值 = 1。
202:中间数位 0 是谷,波动值 = 1。
范围内所有其他数字的波动值均为 0。
因此,总波动值为 1 + 1 + 1 = 3。

示例 3:

输入: num1 = 4848, num2 = 4848
输出:
解释:
数字 4848:第二个数位 8 是峰,第三个数位 4 是谷,波动值为 2。

说明:

  • 1 <= num1 <= num2 <= 10^5

思路

定义数字的波动值是数字中峰与谷的总数,峰指左右两侧的数字严格小于当前数字,谷指左右两侧的数字严格大于当前数字。求 [nums1, nums2] 中数字波动值的总和。

暴力计算每个数字的波动值。

代码


/**
 * @date 2026-06-04 9:04
 */
public class TotalWaviness3751 {

    public int totalWaviness(int num1, int num2) {
        int res = 0;
        for (int i = num1; i <= num2; i++) {
            String s = String.valueOf(i);
            for (int j = 1; j < s.length() - 1; j++) {
                if ((s.charAt(j) > s.charAt(j - 1) && s.charAt(j) > s.charAt(j + 1)) ||
                        ((s.charAt(j) < s.charAt(j - 1) && s.charAt(j) < s.charAt(j + 1)))) {
                    res++;
                }
            }
        }
        return res;
    }

}

性能

3633.最早完成陆地和水上游乐设施的时间I

目标

给你两种类别的游乐园项目:陆地游乐设施 和 水上游乐设施。

  • 陆地游乐设施
    • landStartTime[i] – 第 i 个陆地游乐设施最早可以开始的时间。
    • landDuration[i] – 第 i 个陆地游乐设施持续的时间。
  • 水上游乐设施
    • waterStartTime[j] – 第 j 个水上游乐设施最早可以开始的时间。
    • waterDuration[j] – 第 j 个水上游乐设施持续的时间。

一位游客必须从 每个 类别中体验 恰好一个 游乐设施,顺序 不限 。

  • 游乐设施可以在其开放时间开始,或 之后任意时间 开始。
  • 如果一个游乐设施在时间 t 开始,它将在时间 t + duration 结束。
  • 完成一个游乐设施后,游客可以立即乘坐另一个(如果它已经开放),或者等待它开放。

返回游客完成这两个游乐设施的 最早可能时间 。

示例 1:

输入:landStartTime = [2,8], landDuration = [4,1], waterStartTime = [6], waterDuration = [3]
输出:9
解释:
方案 A(陆地游乐设施 0 → 水上游乐设施 0):
在时间 landStartTime[0] = 2 开始陆地游乐设施 0。在 2 + landDuration[0] = 6 结束。
水上游乐设施 0 在时间 waterStartTime[0] = 6 开放。立即在时间 6 开始,在 6 + waterDuration[0] = 9 结束。
方案 B(水上游乐设施 0 → 陆地游乐设施 1):
在时间 waterStartTime[0] = 6 开始水上游乐设施 0。在 6 + waterDuration[0] = 9 结束。
陆地游乐设施 1 在 landStartTime[1] = 8 开放。在时间 9 开始,在 9 + landDuration[1] = 10 结束。
方案 C(陆地游乐设施 1 → 水上游乐设施 0):
在时间 landStartTime[1] = 8 开始陆地游乐设施 1。在 8 + landDuration[1] = 9 结束。
水上游乐设施 0 在 waterStartTime[0] = 6 开放。在时间 9 开始,在 9 + waterDuration[0] = 12 结束。
方案 D(水上游乐设施 0 → 陆地游乐设施 0):
在时间 waterStartTime[0] = 6 开始水上游乐设施 0。在 6 + waterDuration[0] = 9 结束。
陆地游乐设施 0 在 landStartTime[0] = 2 开放。在时间 9 开始,在 9 + landDuration[0] = 13 结束。
方案 A 提供了最早的结束时间 9。

示例 2:

输入:landStartTime = [5], landDuration = [3], waterStartTime = [1], waterDuration = [10]
输出:14
解释:
方案 A(水上游乐设施 0 → 陆地游乐设施 0):
在时间 waterStartTime[0] = 1 开始水上游乐设施 0。在 1 + waterDuration[0] = 11 结束。
陆地游乐设施 0 在 landStartTime[0] = 5 开放。立即在时间 11 开始,在 11 + landDuration[0] = 14 结束。
方案 B(陆地游乐设施 0 → 水上游乐设施 0):
在时间 landStartTime[0] = 5 开始陆地游乐设施 0。在 5 + landDuration[0] = 8 结束。
水上游乐设施 0 在 waterStartTime[0] = 1 开放。立即在时间 8 开始,在 8 + waterDuration[0] = 18 结束。
方案 A 提供了最早的结束时间 14。​​​​​​​

说明:

  • 1 <= n, m <= 100
  • landStartTime.length == landDuration.length == n
  • waterStartTime.length == waterDuration.length == m
  • 1 <= landStartTime[i], landDuration[i], waterStartTime[j], waterDuration[j] <= 1000

思路

有两种游乐场项目,landStartTime[i]landDuration[i] 分别表示陆上项目 i 的开始时间与持续时间,waterStartTime[i]waterDuration[i] 分别表示水上项目 i 的开始时间与持续时间。游客需要分别游玩一个陆上项目和一个水上项目,返回最早的结束时间。

暴力枚举每一个陆上项目,与每一个水上项目的开始结束区间比较,如果不相交,结束时间是最大的结束时间,否则最早结束时间为最早的开始时间加上它们的持续时间。

代码


/**
 * @date 2026-06-02 23:45
 */
public class EarliestFinishTime3633 {

    public int earliestFinishTime(int[] landStartTime, int[] landDuration, int[] waterStartTime, int[] waterDuration) {
        int n = landStartTime.length;
        int m = waterStartTime.length;
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            int start = landStartTime[i];
            int end = landStartTime[i] + landDuration[i];
            for (int j = 0; j < m; j++) {
                int ws = waterStartTime[j];
                int we = ws + waterDuration[j];
                if (start >= we) {
                    res = Math.min(res, end);
                } else if (end <= ws) {
                    res = Math.min(res, we);
                } else {
                    res = Math.min(res, Math.min(ws, start) + landDuration[i] + waterDuration[j]);
                }
            }
        }
        return res;
    }
}

性能