LeetCode2521~2540题目解析

LeetCode 2521-2540. 题解合集

文章目录


2521. Distinct Prime Factors of Product of Array(数组乘积中的不同质因数数目)

题目描述

给你一个正整数数组 nums,对 nums 所有元素求积之后,找出并返回乘积中不同质因数的数目。

质数是指大于 1 且仅能被 1 及自身整除的数字。如果 val2 / val1 是一个整数,则整数 val1 是

另一个整数 val2 的一个因数。

示例 1

复制代码
`nums = [2,4,3,7,10,6]` → 4(10080 = 2^5 × 3^2 × 5 × 7)

示例 2

复制代码
`nums = [2,4,8,16]` → 1(1024 = 2^10)

提示:

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

解题思路

解法一:逐个元素试除分解(主提交版本)

  • 对每个 numsi 用试除法分解:因子 d 从 2 到 sqrt(x),若能整除则加入集合并把 x 除以 d 直到
    除不尽,最后 x > 1 时加入集合。返回集合大小。
  • 复杂度 O(n × sqrt(max)),max ≤ 1000 时极快。
    解法二:SPF 最小质因子分解(对照)
  • 预筛 1...1000 的每个数的最小质因子 spf,分解每个元素时用 spf 快速取出质因子加入集合。
    复杂度:
  • 时间复杂度:O(n * sqrt(1000))
  • 空间复杂度:O(不同质因子数)

代码实现

java 复制代码
import java.util.HashSet;
import java.util.Set;

public class Solution {
    // 解法一:逐个元素试除分解(主提交版本)
    public int distinctPrimeFactors(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int x : nums) {
            int v = x;
            for (int d = 2; d * d <= v; d++) {
                if (v % d == 0) {
                    set.add(d);
                    while (v % d == 0) {
                        v /= d;
                    }
                }
            }
            if (v > 1) {
                set.add(v);
            }
        }
        return set.size();
    }

    // 解法二:SPF 最小质因子分解(对照)
    public int distinctPrimeFactorsSpf(int[] nums) {
        int maxN = 1000;
        int[] spf = new int[maxN + 1];
        java.util.List<Integer> primes = new java.util.ArrayList<>();
        for (int i = 2; i <= maxN; i++) {
            if (spf[i] == 0) {
                spf[i] = i;
                primes.add(i);
            }
            for (int p : primes) {
                if (p > spf[i] || i * p > maxN) {
                    break;
                }
                spf[i * p] = p;
            }
        }
        Set<Integer> set = new HashSet<>();
        for (int x : nums) {
            int v = x;
            while (v > 1) {
                set.add(spf[v]);
                v /= spf[v];
            }
        }
        return set.size();
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.distinctPrimeFactors(new int[]{2, 4, 3, 7, 10, 6}) == 4 : "2521 例1 主";
        assert sol.distinctPrimeFactorsSpf(new int[]{2, 4, 3, 7, 10, 6}) == 4 : "2521 例1 对照";
        assert sol.distinctPrimeFactors(new int[]{2, 4, 8, 16}) == 1 : "2521 例2 主";
        assert sol.distinctPrimeFactorsSpf(new int[]{2, 4, 8, 16}) == 1 : "2521 例2 对照";
        assert sol.distinctPrimeFactors(new int[]{1000}) == 2 : "2521 附加 1000=2^3*5^3";
        assert sol.distinctPrimeFactors(new int[]{999}) == 3 : "2521 附加 999=3^3*37";

        // 随机对拍
        java.util.Random rnd = new java.util.Random(2121);
        for (int t = 0; t < 1000; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = 2 + rnd.nextInt(999);
            }
            int a = sol.distinctPrimeFactors(arr);
            int b = sol.distinctPrimeFactorsSpf(arr);
            if (a != b) {
                System.out.println("对拍失败 " + java.util.Arrays.toString(arr) + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2521 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(n * sqrt(1000))
  • 空间复杂度:O(不同质因子数)

2522. Partition String Into Substrings With Values at Most K

题目描述

(将字符串分割成值不超过 K 的子字符串)

给你一个字符串 s,它每一位都是 '1' 到 '9' 之间的数字组成,同时给你一个整数 k。

如果一个字符串 s 的分割满足以下条件,我们称它是一个好分割:

  • s 中每个数位恰好属于一个子字符串;
  • 每个子字符串的值都小于等于 k。
    请你返回 s 所有的好分割中,子字符串的最少数目。如果不存在 s 的好分割,返回 -1。
    一个字符串的值是这个字符串对应的整数;子字符串是字符串中一段连续的字符序列。

示例 1

复制代码
`s = "165462", k = 60` → 4("16","54","6","2")

示例 2

复制代码
`s = "238182", k = 5` → -1

提示:

  • 1 <= s.length <= 10^5
  • s[i] 是 '1' 到 '9' 之间的数字
  • 1 <= k <= 10^9

解题思路

解法一:贪心------每次取尽可能长的合法段(主提交版本)

  • 从前往后扫描,维护当前段的值 val(long 防溢出)。对每个字符:
    若 newVal = oldVal * 10 + digit <= k 则并入当前段;否则当前段结束、以该字符开新段并段数 +1。
  • 若单个数字字符 digit > k,则任何分割都无法容纳该字符,直接返回 -1。
  • 正确性:段越长则段数越少,贪心取最长合法段全局最优,任何提前切段只会增加段数。
  • 复杂度 O(n)。
    解法二:动态规划(对照)
  • dpi = 分割 s0...i-1 的最少段数,dp0 = 0。
  • 对每个 i 枚举最后一段的起点 j(j 从 i-1 往前),维护子串值 val(long),
    若 val <= k 则 dpi = min(dpi, dpj + 1);val 一旦超过 k 就 break(往前只会更大)。
  • 仅用于小数据对拍。
    复杂度:
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

代码实现

java 复制代码
import java.util.Arrays;

public class Solution {
    // 解法一:贪心取最长段(主提交版本)
    public int minimumPartition(String s, int k) {
        int n = s.length();
        int cnt = 0;      // 已完成切割的段数
        long cur = 0;     // 当前段值
        for (int i = 0; i < n; i++) {
            int d = s.charAt(i) - '0';
            if (d > k) {
                return -1;
            }
            long nxt = cur * 10 + d;
            if (nxt <= k) {
                cur = nxt;
            } else {
                cnt++;
                cur = d;
            }
        }
        return cnt + 1; // 最后一段未计入 cnt
    }

    // 解法二:动态规划(对照,小数据)
    public int minimumPartitionDp(String s, int k) {
        int n = s.length();
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE / 2);
        dp[0] = 0;
        for (int i = 1; i <= n; i++) {
            long val = 0;
            long p = 1;
            for (int j = i - 1; j >= 0; j--) {
                val = (long) (s.charAt(j) - '0') * p + val;
                p *= 10;
                if (val > k) {
                    break; // 再往前拼接只会更大
                }
                dp[i] = Math.min(dp[i], dp[j] + 1);
            }
        }
        return dp[n] >= Integer.MAX_VALUE / 2 ? -1 : dp[n];
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.minimumPartition("165462", 60) == 4 : "2522 例1 主";
        assert sol.minimumPartitionDp("165462", 60) == 4 : "2522 例1 对照";
        assert sol.minimumPartition("238182", 5) == -1 : "2522 例2 主";
        assert sol.minimumPartitionDp("238182", 5) == -1 : "2522 例2 对照";
        assert sol.minimumPartition("1234", 10) == 3 : "2522 附加 1|2|34";
        assert sol.minimumPartition("9", 9) == 1 : "2522 附加 单字符";

        // 随机对拍
        java.util.Random rnd = new java.util.Random(2222);
        for (int t = 0; t < 300; t++) {
            int n = 1 + rnd.nextInt(10);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < n; i++) {
                sb.append(1 + rnd.nextInt(9));
            }
            int k = 1 + rnd.nextInt(1000);
            int a = sol.minimumPartition(sb.toString(), k);
            int b = sol.minimumPartitionDp(sb.toString(), k);
            if (a != b) {
                System.out.println("对拍失败 s=" + sb + " k=" + k + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2522 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

2523. Closest Prime Numbers in Range(范围内最接近的两个质数)

题目描述

给你两个正整数 left 和 right,请你找到两个整数 num1 和 num2,它们满足:

  • left <= num1 < num2 <= right;
  • num1 和 num2 都是质数;
  • num2 - num1 是满足上述条件的质数对中的最小值。
    请你返回正整数数组 ans = `num1`, `num2`。如果有多个整数对满足上述条件,请你返回 num1 最小的质数对;
    如果不存在符合题意的质数对,返回 -1, -1。

示例 1

复制代码
`left = 10, right = 19` → [11,13]

示例 2

复制代码
`left = 4, right = 6` → [-1,-1]

提示:

  • 1 <= left <= right <= 10^6

解题思路

解法一:埃氏筛 + 相邻质数扫描(主提交版本)

  • 用埃氏筛预处理 1, right 内所有数是否为质数(布尔数组)。
  • 遍历 left, right 中所有质数,记录相邻质数对的最小差值;若差值相等取 num1 更小的。
  • 复杂度 O(right log log right)。
    解法二:试除法逐个判定(对照)
  • 对区间内每个数用 sqrt 试除判定质数,收集质数列表后扫描相邻对。
  • 复杂度 O(√right × 区间长度),计算量大但作为小范围对照可行。
    复杂度:
  • 时间复杂度:O(right log log right)
  • 空间复杂度:O(right)

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    // 解法一:埃氏筛(主提交版本)
    public int[] closestPrimes(int left, int right) {
        boolean[] isPrime = new boolean[right + 1];
        Arrays.fill(isPrime, 2, right + 1, true);
        for (int i = 2; (long) i * i <= right; i++) {
            if (isPrime[i]) {
                for (int j = i * i; j <= right; j += i) {
                    isPrime[j] = false;
                }
            }
        }
        int bestDiff = Integer.MAX_VALUE;
        int n1 = -1, n2 = -1;
        int prev = -1;
        for (int x = left; x <= right; x++) {
            if (!isPrime[x]) {
                continue;
            }
            if (prev != -1) {
                int diff = x - prev;
                if (diff < bestDiff) {
                    bestDiff = diff;
                    n1 = prev;
                    n2 = x;
                }
                // 差值相等时保留 num1 更小的,即不更新(prev 已经是较小的)
            }
            prev = x;
        }
        return new int[]{n1, n2};
    }

    // 解法二:试除法判定(对照)
    public int[] closestPrimesTrial(int left, int right) {
        List<Integer> primes = new ArrayList<>();
        for (int x = Math.max(2, left); x <= right; x++) {
            if (isPrime(x)) {
                primes.add(x);
            }
        }
        if (primes.size() < 2) {
            return new int[]{-1, -1};
        }
        int bestDiff = Integer.MAX_VALUE;
        int n1 = -1, n2 = -1;
        for (int i = 0; i + 1 < primes.size(); i++) {
            int diff = primes.get(i + 1) - primes.get(i);
            if (diff < bestDiff) {
                bestDiff = diff;
                n1 = primes.get(i);
                n2 = primes.get(i + 1);
            }
        }
        return new int[]{n1, n2};
    }

    private boolean isPrime(int x) {
        if (x < 2) {
            return false;
        }
        for (int d = 2; (long) d * d <= x; d++) {
            if (x % d == 0) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert Arrays.equals(sol.closestPrimes(10, 19), new int[]{11, 13}) : "2523 例1 主";
        assert Arrays.equals(sol.closestPrimesTrial(10, 19), new int[]{11, 13}) : "2523 例1 对照";
        assert Arrays.equals(sol.closestPrimes(4, 6), new int[]{-1, -1}) : "2523 例2 主";
        assert Arrays.equals(sol.closestPrimesTrial(4, 6), new int[]{-1, -1}) : "2523 例2 对照";
        assert Arrays.equals(sol.closestPrimes(1, 1), new int[]{-1, -1}) : "2523 附加 无质数";
        assert Arrays.equals(sol.closestPrimes(2, 3), new int[]{2, 3}) : "2523 附加 最小对";
        assert Arrays.equals(sol.closestPrimes(19, 31), new int[]{29, 31}) : "2523 附加 19-31";

        // 随机对拍
        java.util.Random rnd = new java.util.Random(2323);
        for (int t = 0; t < 1000; t++) {
            int a = 1 + rnd.nextInt(5000);
            int b = a + rnd.nextInt(2000);
            int[] r1 = sol.closestPrimes(a, b);
            int[] r2 = sol.closestPrimesTrial(a, b);
            if (!Arrays.equals(r1, r2)) {
                System.out.println("对拍失败 left=" + a + " right=" + b + " 主=" + Arrays.toString(r1) + " 对照=" + Arrays.toString(r2));
                System.exit(1);
            }
        }
        System.out.println("2523 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(right log log right)
  • 空间复杂度:O(right)

2524. Maximum Frequency Score of a Subarray(子数组的最大频率分数)

题目描述

给定一个整数数组 nums 和一个正整数 k。

数组的频率得分是数组中不同值的幂次之和,并将和对 10^9 + 7 取模。

例如,数组 5,4,5,7,4,4 的频率得分为 (4^3 + 5^2 + 7^1) mod (10^9+7) = 96。

返回 nums 中长度为 k 的子数组的最大频率得分(取模后的最大值)。

子数组是一个数组的连续部分。

示例 1

复制代码
`nums = [1,1,1,2,1,2], k = 3` → 5(子数组 [2,1,2]:2^2 + 1^1 = 5)

示例 2

复制代码
`nums = [1,1,1,1,1,1], k = 4` → 1

提示:

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

解题思路

解法一:滑动窗口 + 幂表维护(主提交版本)

  • 对固定长度 k 的窗口维护频率表 freq 与当前分数 score = Σ pow(v, freqv)。
  • 窗口右移一格:
    a) 移出旧值 v:先减 pow(v, freqv),频率减 1;若新频率 > 0 再加回 pow(v, freqv-1);
    b) 加入新值 v:先减 pow(v, freqv)(若已存在),频率加 1,再加 pow(v, freqv+1)。
  • pow 用快速幂 O(log MOD);总复杂度 O(n log MOD)。
    解法二:暴力重算每个窗口(对照,小数据)
  • 对每个窗口统计频率后重算分数,与主解法交叉验证。
    复杂度:
  • 时间复杂度:O(n log MOD)
  • 空间复杂度:O(k)

代码实现

java 复制代码
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class Solution {
    private static final int MOD = 1_000_000_007;

    // 解法一:滑动窗口维护频率分数(主提交版本)
    public int maxFrequencyScore(int[] nums, int k) {
        int n = nums.length;
        Map<Integer, Integer> freq = new HashMap<>();
        long score = 0;
        // 初始窗口
        for (int i = 0; i < k; i++) {
            int v = nums[i];
            int oldf = freq.getOrDefault(v, 0);
            if (oldf > 0) {
                score = (score - pow(v, oldf) + MOD) % MOD;
            }
            freq.put(v, oldf + 1);
            score = (score + pow(v, oldf + 1)) % MOD;
        }
        long best = score;
        for (int i = k; i < n; i++) {
            // 移出 nums[i-k]
            int out = nums[i - k];
            int fo = freq.get(out);
            score = (score - pow(out, fo) + MOD) % MOD;
            if (fo == 1) {
                freq.remove(out);
            } else {
                freq.put(out, fo - 1);
                score = (score + pow(out, fo - 1)) % MOD;
            }
            // 加入 nums[i]
            int in = nums[i];
            int oldf = freq.getOrDefault(in, 0);
            if (oldf > 0) {
                score = (score - pow(in, oldf) + MOD) % MOD;
            }
            freq.put(in, oldf + 1);
            score = (score + pow(in, oldf + 1)) % MOD;
            best = Math.max(best, score);
        }
        return (int) best;
    }

    private long pow(long a, long b) {
        a %= MOD;
        long res = 1;
        while (b > 0) {
            if ((b & 1) == 1) {
                res = res * a % MOD;
            }
            a = a * a % MOD;
            b >>= 1;
        }
        return res;
    }

    // 解法二:暴力重算每个窗口(对照,小数据)
    public int maxFrequencyScoreBrute(int[] nums, int k) {
        int n = nums.length;
        long best = 0;
        for (int i = 0; i + k <= n; i++) {
            Map<Integer, Integer> f = new HashMap<>();
            for (int j = i; j < i + k; j++) {
                f.merge(nums[j], 1, Integer::sum);
            }
            long s = 0;
            for (Map.Entry<Integer, Integer> e : f.entrySet()) {
                s = (s + pow(e.getKey(), e.getValue())) % MOD;
            }
            best = Math.max(best, s);
        }
        return (int) best;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.maxFrequencyScore(new int[]{1, 1, 1, 2, 1, 2}, 3) == 5 : "2524 例1 主";
        assert sol.maxFrequencyScoreBrute(new int[]{1, 1, 1, 2, 1, 2}, 3) == 5 : "2524 例1 对照";
        assert sol.maxFrequencyScore(new int[]{1, 1, 1, 1, 1, 1}, 4) == 1 : "2524 例2 主";
        assert sol.maxFrequencyScoreBrute(new int[]{1, 1, 1, 1, 1, 1}, 4) == 1 : "2524 例2 对照";
        assert sol.maxFrequencyScore(new int[]{2, 2, 2}, 2) == 4 : "2524 附加 幂次";
        assert sol.maxFrequencyScore(new int[]{1, 2, 3}, 1) == 3 : "2524 附加 k=1";

        // 随机对拍
        Random rnd = new Random(2424);
        for (int t = 0; t < 300; t++) {
            int n = 1 + rnd.nextInt(15);
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = 1 + rnd.nextInt(6);
            }
            int k = 1 + rnd.nextInt(n);
            int a = sol.maxFrequencyScore(arr, k);
            int b = sol.maxFrequencyScoreBrute(arr, k);
            if (a != b) {
                System.out.println("对拍失败 " + Arrays.toString(arr) + " k=" + k + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2524 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(n log MOD)
  • 空间复杂度:O(k)

2525. Categorize Box According to Criteria(根据规则将箱子分类)

题目描述

给你四个整数 length、width、height 和 mass,分别表示一个箱子的三个维度和质量,

返回一个表示箱子类别的字符串:

  • 如果满足以下条件,那么箱子是 "Bulky" 的:
    箱子至少有一个维度大于等于 10^4,或者箱子的体积大于等于 10^9。
  • 如果箱子的质量大于等于 100,那么箱子是 "Heavy" 的。
  • 同时是 "Bulky" 和 "Heavy" 返回 "Both";都不是返回 "Neither";
    是 "Bulky" 但不是 "Heavy" 返回 "Bulky";是 "Heavy" 但不是 "Bulky" 返回 "Heavy"。
    注意:箱子的体积等于长、宽、高的乘积。

示例 1

复制代码
`length = 1000, width = 35, height = 700, mass = 300` → "Heavy"

示例 2

复制代码
`length = 200, width = 50, height = 800, mass = 50` → "Neither"

提示:

  • 1 <= length, width, height <= 10^5
  • 1 <= mass <= 10^3

解题思路

解法一:条件判断(主提交版本)

  • bulky = max(len,wid,hei) >= 10^4 或 (long)lenwidhei >= 10^9(用 long 防溢出)。
  • heavy = mass >= 100。
  • 按分类规则拼接返回。
    解法二:分支枚举(对照)
  • 用 if-else 按四种情形显式判断,与解法一逻辑等价但写法不同,用于交叉验证。
    复杂度:
  • 时间复杂度:O(1)
  • 空间复杂度:O(1)

代码实现

java 复制代码
import java.util.Random;

public class Solution {
    // 解法一:布尔组合(主提交版本)
    public String categorizeBox(int length, int width, int height, int mass) {
        boolean bulky = length >= 10000 || width >= 10000 || height >= 10000
                || (long) length * width * height >= 1000000000L;
        boolean heavy = mass >= 100;
        if (bulky && heavy) {
            return "Both";
        }
        if (!bulky && !heavy) {
            return "Neither";
        }
        if (bulky) {
            return "Bulky";
        }
        return "Heavy";
    }

    // 解法二:显式分支(对照)
    public String categorizeBoxBranch(int length, int width, int height, int mass) {
        long volume = (long) length * width * height;
        boolean bulky = (length >= 10000 || width >= 10000 || height >= 10000)
                || volume >= 1000000000L;
        boolean heavy = mass >= 100;
        if (bulky && heavy) {
            return "Both";
        } else if (!bulky && !heavy) {
            return "Neither";
        } else if (bulky) {
            return "Bulky";
        } else {
            return "Heavy";
        }
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.categorizeBox(1000, 35, 700, 300).equals("Heavy") : "2525 例1 主";
        assert sol.categorizeBoxBranch(1000, 35, 700, 300).equals("Heavy") : "2525 例1 对照";
        assert sol.categorizeBox(200, 50, 800, 50).equals("Neither") : "2525 例2 主";
        assert sol.categorizeBoxBranch(200, 50, 800, 50).equals("Neither") : "2525 例2 对照";
        assert sol.categorizeBox(10000, 1, 1, 1).equals("Bulky") : "2525 附加 维度";
        assert sol.categorizeBox(1000, 1000, 1000, 1).equals("Bulky") : "2525 附加 体积";
        assert sol.categorizeBox(1000, 1000, 1000, 100).equals("Both") : "2525 附加 Both";

        // 随机对拍
        Random rnd = new Random(2525);
        for (int t = 0; t < 300; t++) {
            int l = 1 + rnd.nextInt(15000);
            int w = 1 + rnd.nextInt(15000);
            int h = 1 + rnd.nextInt(15000);
            int m = 1 + rnd.nextInt(150);
            String a = sol.categorizeBox(l, w, h, m);
            String b = sol.categorizeBoxBranch(l, w, h, m);
            if (!a.equals(b)) {
                System.out.println("对拍失败 " + l + "," + w + "," + h + "," + m + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2525 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(1)
  • 空间复杂度:O(1)

2526. Find Consecutive Integers from a Data Stream(找到数据流中的连续整数)

题目描述

给你一个整数数据流,请你实现一个数据结构,检查数据流中最后 k 个整数是否等于给定值 value。

实现 DataStream 类:

  • DataStream(int value, int k):用 value 和 k 初始化一个空的整数数据流。
  • boolean consec(int num):将 num 添加到整数数据流。如果后 k 个整数都等于 value,返回 true,
    否则返回 false。如果少于 k 个整数,条件不满足,也返回 false。

示例

复制代码
["DataStream","consec","consec","consec","consec"]
[[4,3],[4],[4],[4],[3]]
→ [null,false,false,true,false]

提示:

  • 1 <= value, num <= 10^9
  • 1 <= k <= 10^5
  • 至多调用 consec 10^5 次

解题思路

解法一:仅维护「末尾连续等于 value 的个数」(主提交版本)

  • 事实上并不需要记住整个窗口:只要记录从末尾往前连续等于 value 的个数 cnt。
    对每个新数 num:若 num == value 则 cnt++,否则 cnt = 0;
    返回 cnt >= k。
  • 复杂度 O(1) 每次。
    解法二:队列显式模拟(对照)
  • 用容量 k 的队列存最近 k 个整数,同时统计队列内等于 value 的数量 eq;
    入队时若队列满则出队并更新 eq,再入队新数更新 eq,返回 eq == k。
    (队列满时 eqk 且队列长度k 等价于最后 k 个都等于 value)
    复杂度:
  • 时间复杂度:O(1) 每操作
  • 空间复杂度:O(1)(解法一)或 O(k)(解法二)

代码实现

java 复制代码
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Random;

public class Solution {
    private final int value;
    private final int k;
    private int cnt;

    public Solution() {
        this.value = 0;
        this.k = 0;
    }

    // 解法一:连续相等计数
    public Solution(int value, int k) {
        this.value = value;
        this.k = k;
        this.cnt = 0;
    }

    public boolean consec(int num) {
        if (num == value) {
            cnt++;
        } else {
            cnt = 0;
        }
        return cnt >= k;
    }

    // 解法二:队列模拟(照应类)
    public static class DataStreamQueue {
        private final int value;
        private final int k;
        private final Queue<Integer> q;
        private int eq;

        public DataStreamQueue(int value, int k) {
            this.value = value;
            this.k = k;
            this.q = new ArrayDeque<>();
            this.eq = 0;
        }

        public boolean consec(int num) {
            if (q.size() == k) {
                int out = q.poll();
                if (out == value) {
                    eq--;
                }
            }
            q.offer(num);
            if (num == value) {
                eq++;
            }
            return eq == k;
        }
    }

    public static void main(String[] args) {
        // 官方示例
        Solution ds = new Solution(4, 3);
        assert !ds.consec(4);
        assert !ds.consec(4);
        assert ds.consec(4);
        assert !ds.consec(3);

        DataStreamQueue dsq = new DataStreamQueue(4, 3);
        assert !dsq.consec(4);
        assert !dsq.consec(4);
        assert dsq.consec(4);
        assert !dsq.consec(3);

        // 随机对拍
        Random rnd = new Random(2626);
        for (int t = 0; t < 300; t++) {
            int value = 1 + rnd.nextInt(5);
            int k = 1 + rnd.nextInt(6);
            Solution a = new Solution(value, k);
            DataStreamQueue b = new DataStreamQueue(value, k);
            int steps = 1 + rnd.nextInt(50);
            for (int s = 0; s < steps; s++) {
                int num = 1 + rnd.nextInt(6);
                boolean ra = a.consec(num);
                boolean rb = b.consec(num);
                if (ra != rb) {
                    System.out.println("对拍失败 value=" + value + " k=" + k + " step=" + s + " num=" + num);
                    System.exit(1);
                }
            }
        }
        System.out.println("2526 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(1) 每操作
  • 空间复杂度:O(1)(解法一)或 O(k)(解法二)

2527. Find Xor-Beauty of Array(查询数组异或美丽值)

题目描述

给你一个下标从 0 开始的整数数组 nums。

三个下标 i、j 和 k 的有效值定义为 ((nums[i] | nums[j]) & nums[k])。

一个数组的异或美丽值是数组中所有满足 0 <= i, j, k < n 的三元组 (i, j, k) 的有效值的异或结果。

请你返回 nums 的异或美丽值。

示例 1

复制代码
`nums = [1,4]` → 5(8 个三元组异或后为 5)

示例 2

复制代码
`nums = [15,45,20,2,34,35,5,44,32,30]` → 34

提示:

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

解题思路

解法一:按位分析化简(主提交版本)

  • 目标是对所有 (i,j,k) 计算 v = (x|y)&z 并全部异或。按位独立:考虑第 b 位。
  • 记 c = 原数组中第 b 位为 1 的元素个数,z0 = c,z1 = n - c。
    对固定 (i,j,k):若 k 的位是 0,则 v 的位恒为 0;若 k 的位是 1,则 v 的位 = (x|y) 的位,
    即 i、j 中至少一个的该位为 1。
  • 第 b 位所有三元组异或结果为 1 ⟺ 贡献为 1 的三元组个数为奇数。
    贡献 1 的三元组数 = (k 取位 1 的个数) × (i,j 至少一个位 1 的组合数)
    = z1 × (n^2 - z0^2)。
  • 该项为奇数时答案第 b 位为 1。由于 z1、n^2 - z0^2 的具体奇偶可以直接按位与 1 判断。
  • 复杂度 O(n × 31)。
    解法二:三重循环暴力(对照,小数据)
  • 对 n ≤ 60 的小数组直接三重循环枚举三元组求异或。
    复杂度:
  • 时间复杂度:O(n * 31)
  • 空间复杂度:O(1)

代码实现

java 复制代码
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class Solution {
    // 解法一:按位计数化简(主提交版本)
    public int xorBeauty(int[] nums) {
        int n = nums.length;
        int ans = 0;
        for (int b = 0; b < 31; b++) {
            long c = 0; // 该位为 1 的个数
            for (int x : nums) {
                if ((x >> b & 1) == 1) {
                    c++;
                }
            }
            // k 取到该位为 1 的数:c 种;i,j 至少一个该位为 1:n^2 - (n-c)^2
            long cnt = c * (n * n - (n - c) * (n - c));
            if ((cnt & 1) == 1) {
                ans |= 1 << b;
            }
        }
        return ans;
    }

    // 解法二:暴力三重循环(对照,小数据)
    public int xorBeautyBrute(int[] nums) {
        int n = nums.length;
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    ans ^= ((nums[i] | nums[j]) & nums[k]);
                }
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.xorBeauty(new int[]{1, 4}) == 5 : "2527 例1 主";
        assert sol.xorBeautyBrute(new int[]{1, 4}) == 5 : "2527 例1 对照";
        assert sol.xorBeauty(new int[]{15, 45, 20, 2, 34, 35, 5, 44, 32, 30}) == 34 : "2527 例2 主";
        assert sol.xorBeauty(new int[]{7}) == 7 : "2527 附加 单元素";

        // 随机对拍(小数据)
        Random rnd = new Random(2727);
        for (int t = 0; t < 200; t++) {
            int n = 1 + rnd.nextInt(7);
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = 1 + rnd.nextInt(30);
            }
            int a = sol.xorBeauty(arr);
            int b = sol.xorBeautyBrute(arr);
            if (a != b) {
                System.out.println("对拍失败 " + java.util.Arrays.toString(arr) + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2527 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(n * 31)
  • 空间复杂度:O(1)

2528. Maximize the Minimum Powered City(最大化城市的最小电量)

题目描述

给你一个下标从 0 开始长度为 n 的整数数组 stations,其中 stations[i] 表示第 i 座城市的供电站数目。

每个供电站可以在一定范围内给所有城市提供电力:如果范围是 r,在城市 i 处的供电站可以给所有满足

|i-j| <= r 且 0 <= i, j <= n-1 的城市 j 供电。一座城市的电量是所有能给它供电的供电站数目。

政府批准了可以额外建造 k 座供电站,需要决定建在哪里,这些供电站与已有的供电站有相同的供电范围 r。

给定 r 和 k,如果以最优策略建造额外的发电站,返回所有城市中最小电量的最大值。

示例 1

复制代码
`stations = [1,2,4,5,0], r = 1, k = 2` → 5

示例 2

复制代码
`stations = [4,4,4,4], r = 0, k = 3` → 4

提示:

  • 1 <= n <= 10^5
  • 0 <= stations[i] <= 10^5
  • 0 <= r <= n-1
  • 0 <= k <= 10^9

解题思路

解法一:二分答案 + 前缀和滑动窗口 + 差分贪心(主提交版本)

  • 先求每座城市的初始电量 poweri = Σ stationsj(|j-i| <= r),用滑动窗口 O(n)。
  • 二分目标 x:检查能否用不超过 k 座新站让每座城市电量 >= x。
  • 检查(差分贪心):从左到右扫,cur 为当前位置生效的新站数(差分数组 diff 维护)。
    若 poweri + cur < x,则在位置 i+r(能覆盖 i 的最靠右建站点,尽量造福右侧)补 need 座站:
    cur += need、diffmin(n-1, i+2r+1) -= need,累计 used += need;used > k 则失败。
  • 复杂度 O(n log(Σstations + k))。
    解法二:全枚举建站方案(对照,仅小数据)
  • 将 k 座站分配到 n 个位置(允许重复,组合数 C(n+k-1, n-1)),逐方案计算电量并取最小值的最大值。
  • 用于小规模交叉验证。
    复杂度:
  • 时间复杂度:O(n log(Σstations + k))
  • 空间复杂度:O(n)

代码实现

java 复制代码
import java.util.Arrays;
import java.util.Random;

public class Solution {
    // 解法一:二分 + 差分贪心(主提交版本)
    public long maxPower(int[] stations, int r, int k) {
        int n = stations.length;
        long[] power = new long[n];
        long win = 0;
        for (int i = 0; i <= r && i < n; i++) {
            win += stations[i];
        }
        power[0] = win;
        for (int i = 1; i < n; i++) {
            int out = i - r - 1;
            int in = i + r;
            if (out >= 0) {
                win -= stations[out];
            }
            if (in < n) {
                win += stations[in];
            }
            power[i] = win;
        }
        long lo = 0, hi = (long) 3e14;
        long ans = 0;
        while (lo <= hi) {
            long mid = (lo + hi) >>> 1;
            if (check(power, r, k, mid)) {
                ans = mid;
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
        return ans;
    }

    private boolean check(long[] power, int r, long k, long x) {
        int n = power.length;
        long[] diff = new long[n + 1];
        long cur = 0;
        long used = 0;
        for (int i = 0; i < n; i++) {
            cur += diff[i];
            long have = power[i] + cur;
            if (have < x) {
                long need = x - have;
                used += need;
                if (used > k) {
                    return false;
                }
                cur += need; // 在 i+r 处建 need 座站,对当前位置立即生效
                int end = i + 2 * r + 1; // 该站影响右边界 + 1
                if (end < n) {
                    diff[end] -= need;
                }
            }
        }
        return true;
    }

    // 解法二:全枚举建站分配(对照,小数据 n <= 6, k <= 5)
    public long maxPowerBrute(int[] stations, int r, int k) {
        int n = stations.length;
        long[] base = new long[n];
        for (int i = 0; i < n; i++) {
            for (int j = Math.max(0, i - r); j <= Math.min(n - 1, i + r); j++) {
                base[i] += stations[j];
            }
        }
        int[] build = new int[n]; // build[p] = 在 p 处建多少座新站
        bestMin = -1;
        enumerate(build, 0, (int) k, n, r, base);
        return bestMin;
    }

    private long bestMin = -1;

    private void enumerate(int[] build, int idx, int remain, int n, int r, long[] base) {
        if (idx == n) {
            if (remain != 0) {
                return;
            }
            long[] pow = base.clone();
            for (int p = 0; p < n; p++) {
                for (int g = 0; g < build[p]; g++) {
                    for (int j = Math.max(0, p - r); j <= Math.min(n - 1, p + r); j++) {
                        pow[j]++;
                    }
                }
            }
            long mn = pow[0];
            for (long v : pow) {
                mn = Math.min(mn, v);
            }
            bestMin = Math.max(bestMin, mn);
            return;
        }
        for (int c = 0; c <= remain; c++) {
            build[idx] = c;
            enumerate(build, idx + 1, remain - c, n, r, base);
        }
        build[idx] = 0;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.maxPower(new int[]{1, 2, 4, 5, 0}, 1, 2) == 5 : "2528 例1 主";
        assert sol.maxPower(new int[]{4, 4, 4, 4}, 0, 3) == 4 : "2528 例2 主";
        assert sol.maxPower(new int[]{1, 1, 1, 1}, 1, 0) == 2 : "2528 附加 k=0";
        assert sol.maxPower(new int[]{0, 0, 0}, 1, 5) == 3 : "2528 附加 全零";

        // 随机对拍(小数据全枚举验证)
        Random rnd = new Random(2828);
        for (int t = 0; t < 300; t++) {
            int n = 1 + rnd.nextInt(6);
            int[] st = new int[n];
            for (int i = 0; i < n; i++) {
                st[i] = rnd.nextInt(6);
            }
            int r = rnd.nextInt(n);
            int k = rnd.nextInt(6);
            long a = sol.maxPower(st, r, k);
            long b = sol.maxPowerBrute(st, r, k);
            if (a != b) {
                System.out.println("对拍失败 " + Arrays.toString(st) + " r=" + r + " k=" + k + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2528 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(n log(Σstations + k))
  • 空间复杂度:O(n)

2529. Maximum Count of Positive Integer and Negative Integer(正整数和负整数的最大计数)

题目描述

给你一个按非递减顺序排列的数组 nums,返回正整数数目和负整数数目中的最大值。

如果 nums 中正整数的数目是 pos,而负整数的数目是 neg,返回 pos 和 neg 二者中的最大值。

注意:0 既不是正整数也不是负整数。

进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1

复制代码
`nums = [-2,-1,-1,1,2,3]` → 3

示例 2

复制代码
`nums = [-3,-2,-1,0,0,1,2]` → 3

示例 3

复制代码
`nums = [5,20,66,1314]` → 4

提示:

  • 1 <= nums.length <= 2000
  • -2000 <= nums[i] <= 2000
  • 非递减顺序排列

解题思路

解法一:一次遍历计数(主提交版本)

  • 遍历统计 neg(< 0)与 pos(> 0)的数量,返回较大者。O(n)。
    解法二:二分查找边界(对照,满足 O(log n) 进阶)
  • 用手写二分求第一个 >= 0 的下标 firstNonNeg 与第一个 > 0 的下标 firstPos:
    neg = firstNonNeg,pos = n - firstPos,返回 max。
    复杂度:
  • 时间复杂度:O(n)(解法二 O(log n))
  • 空间复杂度:O(1)

代码实现

java 复制代码
import java.util.Arrays;

public class Solution {
    // 解法一:一次遍历(主提交版本)
    public int maximumCount(int[] nums) {
        int neg = 0, pos = 0;
        for (int x : nums) {
            if (x < 0) {
                neg++;
            } else if (x > 0) {
                pos++;
            }
        }
        return Math.max(neg, pos);
    }

    // 解法二:二分查找边界(对照)
    public int maximumCountBinary(int[] nums) {
        int n = nums.length;
        // 第一个 >= 0 的下标
        int firstNonNeg = lowerBound(nums, 0);
        // 第一个 > 0 的下标
        int firstPos = upperBound(nums, 0);
        int neg = firstNonNeg;
        int pos = n - firstPos;
        return Math.max(neg, pos);
    }

    private int lowerBound(int[] a, int target) {
        int lo = 0, hi = a.length;
        while (lo < hi) {
            int mid = (lo + hi) >>> 1;
            if (a[mid] < target) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        return lo;
    }

    private int upperBound(int[] a, int target) {
        int lo = 0, hi = a.length;
        while (lo < hi) {
            int mid = (lo + hi) >>> 1;
            if (a[mid] <= target) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        return lo;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();

        assert sol.maximumCount(new int[]{-2, -1, -1, 1, 2, 3}) == 3 : "2529 例1 主";
        assert sol.maximumCountBinary(new int[]{-2, -1, -1, 1, 2, 3}) == 3 : "2529 例1 对照";
        assert sol.maximumCount(new int[]{-3, -2, -1, 0, 0, 1, 2}) == 3 : "2529 例2 主";
        assert sol.maximumCountBinary(new int[]{-3, -2, -1, 0, 0, 1, 2}) == 3 : "2529 例2 对照";
        assert sol.maximumCount(new int[]{5, 20, 66, 1314}) == 4 : "2529 例3 主";
        assert sol.maximumCountBinary(new int[]{5, 20, 66, 1314}) == 4 : "2529 例3 对照";
        assert sol.maximumCount(new int[]{0, 0, 0}) == 0 : "2529 附加 全零";
        assert sol.maximumCount(new int[]{-1, 0, 0}) == 1 : "2529 附加 一负";

        // 随机对拍
        java.util.Random rnd = new java.util.Random(2929);
        for (int t = 0; t < 2000; t++) {
            int n = 1 + rnd.nextInt(60);
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = -5 + rnd.nextInt(11);
            }
            Arrays.sort(arr);
            int a = sol.maximumCount(arr);
            int b = sol.maximumCountBinary(arr);
            if (a != b) {
                System.out.println("对拍失败 " + Arrays.toString(arr) + " 主=" + a + " 对照=" + b);
                System.exit(1);
            }
        }
        System.out.println("2529 全部 PASS");
    }
}

复杂度分析

  • 时间复杂度:O(n)(解法二 O(log n))
  • 空间复杂度:O(1)

2530. Maximal Score After Applying K Operations(执行 K 次操作后的最大分数)

题目描述

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。你的起始分数为 0 。

在一步操作中:选出一个满足 0 <= i < nums.length 的下标 i,将你的分数增加 nums[i],

并将 nums[i] 替换为 ceil(nums[i] / 3)(大于等于 val 的最小整数)。

返回在恰好执行 k 次操作后,你可能获得的最大分数。

示例 1

复制代码
nums = [10,10,10,10,10], k = 5 -> 50

示例 2

复制代码
nums = [1,10,3,3,3], k = 3 -> 17

提示:

  • 1 <= nums.length, k <= 10^5;1 <= nums[i] <= 10^9

解题思路

解法一:贪心 + 最大堆(主提交版本)

每次操作都应当选择当前值最大的元素,

因为它的贡献最大,且替换为 ceil(x/3) 后仍然可能继续被选中。

维护一个大顶堆,每轮弹出最大值加入分数,再把它除以 3 向上取整后放回。

解法二:排序 + 模拟,对照

将数组排序后,每次取出当前位置的最大值,其余用堆结构等价,

此处给出一个朴素的反复取最大值的实现用于对拍。

时间复杂度:解法一 O((n+k) log n);空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.PriorityQueue;

public class Solution {

    public long maxKelements(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> b - a);
        for (int x : nums) {
            pq.offer(x);
        }
        long score = 0;
        for (int i = 0; i < k; i++) {
            int cur = pq.poll();
            score += cur;
            pq.offer((cur + 2) / 3); // ceil(cur / 3)
        }
        return score;
    }

    public long maxKelementsBrute(int[] nums, int k) {
        long[] a = new long[nums.length];
        for (int i = 0; i < nums.length; i++) {
            a[i] = nums[i];
        }
        long score = 0;
        for (int t = 0; t < k; t++) {
            int idx = 0;
            for (int i = 1; i < a.length; i++) {
                if (a[i] > a[idx]) {
                    idx = i;
                }
            }
            score += a[idx];
            a[idx] = (a[idx] + 2) / 3;
        }
        return score;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.maxKelements(new int[]{10, 10, 10, 10, 10}, 5) == 50 ? "PASS" : "FAIL");
        System.out.println(s.maxKelements(new int[]{1, 10, 3, 3, 3}, 3) == 17 ? "PASS" : "FAIL");

        // 随机对拍
        java.util.Random rnd = new java.util.Random(1);
        boolean ok = true;
        for (int t = 0; t < 2000; t++) {
            int n = 1 + rnd.nextInt(8);
            int k = 1 + rnd.nextInt(12);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(30);
            }
            long r1 = s.maxKelements(a, k);
            long r2 = s.maxKelementsBrute(a, k);
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch: k=" + k + " arr=" + java.util.Arrays.toString(a)
                        + " heap=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2530 全部 PASS" : "2530 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O((n+k) log n);空间复杂度:O(n)。


2531. Make Number of Distinct Characters Equal(使字符串中不同字符的数目相等)

题目描述

给你两个下标从 0 开始的字符串 word1 和 word2 。

一次移动由以下两个步骤组成:选中两个下标 i 和 j,分别满足 0 <= i < word1.length

和 0 <= j < word2.length,交换 word1[i] 和 word2[j]。

如果可以通过恰好一次移动,使 word1 和 word2 中不同字符的数目相等,则返回 true;否则返回 false。

示例 1

复制代码
word1 = "ac", word2 = "b" -> false

示例 2

复制代码
word1 = "abcc", word2 = "aab" -> true

示例 3

复制代码
word1 = "abcde", word2 = "fghij" -> true

提示:

  • 1 <= word1.length, word2.length <= 10^5;两个字符串仅由小写英文字母组成。

解题思路

解法一:分类讨论计数(主提交版本)

统计两个字符串的字符频率。

交换一个字符对 (c1, c2)(c1 来自 word1、c2 来自 word2)之后,

word1 的字符集合变化为:若 c1 在 word1 中出现次数为 1(被移除后消失)则总数 -1;

若 c2 原本不在 word1 中则总数 +1。word2 同理。

枚举所有 26*26 的字符对,检查交换后两个不同字符数是否相等。

解法二:双集合暴力模拟,对照

构造字面字符串,尝试所有 i、j 交换后统计

不同字符数,仅用于小数据对拍。

时间复杂度:解法一 O(26^2)(读入 O(len));空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {

    public boolean isItPossible(String word1, String word2) {
        int[] c1 = new int[26];
        int[] c2 = new int[26];
        for (char ch : word1.toCharArray()) c1[ch - 'a']++;
        for (char ch : word2.toCharArray()) c2[ch - 'a']++;
        int d1 = distinct(c1);
        int d2 = distinct(c2);
        for (int a = 0; a < 26; a++) {
            if (c1[a] == 0) continue;
            for (int b = 0; b < 26; b++) {
                if (c2[b] == 0) continue;
                // 从 word1 拿走 a、放入 b;从 word2 拿走 b、放入 a
                int nd1 = d1;
                int nd2 = d2;
                if (c1[a] == 1) nd1--;       // a 在 word1 中消失
                if (c2[b] == 1) nd2--;       // b 在 word2 中消失
                if (c1[b] == 0) nd1++;       // b 新出现在 word1
                if (c2[a] == 0) nd2++;       // a 新出现在 word2
                if (a == b) {
                    // 交换的是同一个字符:两边集合都不变
                    nd1 = d1;
                    nd2 = d2;
                }
                if (nd1 == nd2) return true;
            }
        }
        return false;
    }

    private int distinct(int[] cnt) {
        int d = 0;
        for (int c : cnt) if (c > 0) d++;
        return d;
    }

    public boolean isItPossibleBrute(String word1, String word2) {
        for (int i = 0; i < word1.length(); i++) {
            for (int j = 0; j < word2.length(); j++) {
                StringBuilder a = new StringBuilder(word1);
                StringBuilder b = new StringBuilder(word2);
                char x = a.charAt(i);
                char y = b.charAt(j);
                a.setCharAt(i, y);
                b.setCharAt(j, x);
                if (distinctStr(a.toString()) == distinctStr(b.toString())) return true;
            }
        }
        return false;
    }

    private int distinctStr(String s) {
        boolean[] seen = new boolean[26];
        for (char ch : s.toCharArray()) seen[ch - 'a'] = true;
        int d = 0;
        for (boolean b : seen) if (b) d++;
        return d;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(!s.isItPossible("ac", "b") ? "PASS" : "FAIL");
        System.out.println(s.isItPossible("abcc", "aab") ? "PASS" : "FAIL");
        System.out.println(s.isItPossible("abcde", "fghij") ? "PASS" : "FAIL");

        // 随机对拍
        java.util.Random rnd = new java.util.Random(2);
        boolean ok = true;
        for (int t = 0; t < 2000; t++) {
            StringBuilder a = new StringBuilder();
            StringBuilder b = new StringBuilder();
            int la = 1 + rnd.nextInt(6);
            int lb = 1 + rnd.nextInt(6);
            for (int i = 0; i < la; i++) a.append((char) ('a' + rnd.nextInt(4)));
            for (int i = 0; i < lb; i++) b.append((char) ('a' + rnd.nextInt(4)));
            boolean r1 = s.isItPossible(a.toString(), b.toString());
            boolean r2 = s.isItPossibleBrute(a.toString(), b.toString());
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch: w1=" + a + " w2=" + b + " main=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2531 全部 PASS" : "2531 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O(26^2)(读入 O(len));空间复杂度:O(1)。


2532. Time to Cross a Bridge(过桥的时间)

题目描述

共有 k 位工人计划将 n 个箱子从右侧的(旧)仓库移动到左侧的(新)仓库。

给你两个整数 n 和 k,以及一个二维整数数组 time,大小为 k x 4,

其中 time[i] = `right_i`, `pick_i`, `left_i`, `put_i`:

  • 从左岸(新仓库)跨过桥到右岸(旧仓库),用时 right_i 分钟;
  • 从旧仓库选择一个箱子并返回到桥边,用时 pick_i 分钟(不同工人可同时搬箱);
  • 从右岸(旧仓库)跨过桥到左岸(新仓库),用时 left_i 分钟;
  • 将箱子放入新仓库并返回到桥边,用时 put_i 分钟(不同工人可同时放箱)。
    若 left_i + right_i > left_j + right_j,或相等且 i > j,则称工人 i 效率低于工人 j。
    过桥规则:同时只能有一名工人过桥;当桥梁未被使用时,优先让右侧「效率最低」的工人
    (已经拿起盒子的工人)过桥,否则优先让左侧「效率最低」的工人通过;
    如果左侧已经派出足够的工人来拾取所有剩余的箱子,则不会再从左侧派出工人。
    返回最后一个箱子到达桥左侧的时间。

示例 1

复制代码
n = 1, k = 3, time = [[1,1,2,1],[1,1,3,1],[1,1,4,1]] -> 6

示例 2

复制代码
n = 3, k = 2, time = [[1,5,1,8],[10,10,10,10]] -> 37

提示:

  • 1 <= n, k <= 10^4;1 <= left_i, pick_i, right_i, put_i <= 1000

解题思路

解法一:双优先队列 + 事件队列模拟(主提交版本)

  • left / right 两个等待堆:按「效率低者优先」(left+right 大、下标大)排序;
  • events 事件堆按完成时间升序:(完成时刻, 工人, 类型),类型 0 = 放完箱子回到左侧等待,
    类型 1 = 取完箱子回到右侧等待;
  • 每轮先处理所有已到期事件(把工人放回对应等待堆);
    桥空闲时:右侧有等待工人则让效率最低者过桥到左岸(耗时 left_i,记录答案 = 桥到达时刻),
    否则若还有箱子未取且左侧有等待工人,派效率最低者过桥到右岸(耗时 right_i,取箱数 +1);
    若两边都无人可动,把时间推进到下一个事件发生时刻。
    答案 = 最后一个从右岸返回左岸的过桥完成时刻(每个右→左过桥工人恰好携带一个箱子)。
    解法二:分钟级暴力模拟,对照
    小数据下逐分钟推进,同一时刻先结算已完成的取/放箱,
    再看桥是否空闲、按规则选人过桥,得到答案用于对拍。
    时间复杂度:解法一 O((n + k) log k);空间复杂度:O(k)。

代码实现

java 复制代码
import java.util.PriorityQueue;

public class Solution {

    public int findCrossingTime(int n, int k, int[][] time) {
        long[] eff = new long[k];
        for (int i = 0; i < k; i++) {
            eff[i] = (long) time[i][0] + time[i][2]; // right + left
        }
        PriorityQueue<Integer> left = new PriorityQueue<Integer>((a, b) ->
                eff[a] != eff[b] ? (eff[a] > eff[b] ? -1 : 1) : (b - a));
        PriorityQueue<Integer> right = new PriorityQueue<Integer>((a, b) ->
                eff[a] != eff[b] ? (eff[a] > eff[b] ? -1 : 1) : (b - a));
        for (int i = 0; i < k; i++) left.offer(i);

        // events: [完成时刻, 工人, 类型] 类型 0 = 放完箱子回左侧;1 = 取完箱子回右侧
        PriorityQueue<long[]> events = new PriorityQueue<long[]>((a, b) ->
                a[0] != b[0] ? (a[0] < b[0] ? -1 : 1) : (int) (a[1] - b[1]));

        long cur = 0;
        int remaining = n;   // 尚未被派出取箱的箱子数
        long ans = 0;        // 最后一个右->左过桥完成时刻

        while (remaining > 0 || !right.isEmpty() || !events.isEmpty()) {
            // 1. 结算所有已到期事件
            while (!events.isEmpty() && events.peek()[0] <= cur) {
                long[] ev = events.poll();
                int w = (int) ev[1];
                if (ev[2] == 0) left.offer(w);
                else right.offer(w);
            }
            // 2. 桥空闲时选人过桥
            if (!right.isEmpty()) {
                int w = right.poll();
                cur += time[w][2];          // 右 -> 左,携带一个箱子
                ans = cur;                  // 该箱子到达桥左侧的时刻
                events.offer(new long[]{cur + time[w][3], w, 0}); // 放箱后回左侧
            } else if (remaining > 0 && !left.isEmpty()) {
                int w = left.poll();
                cur += time[w][0];          // 左 -> 右,去取一个箱子
                remaining--;
                events.offer(new long[]{cur + time[w][1], w, 1}); // 取箱后回右侧
            } else {
                // 无人可动:推进到最近的事件时刻
                long next = Long.MAX_VALUE;
                if (!events.isEmpty()) next = Math.min(next, events.peek()[0]);
                if (next == Long.MAX_VALUE) break;
                cur = next;
            }
        }
        return (int) ans;
    }

    public int findCrossingTimeBrute(int n, int k, int[][] time) {
        // side[w] = 0 左岸 / 1 右岸;-1 = 正桥上空闲等待
        int[] side = new int[k];
        // -1 = 空闲;0 = 正在放箱(完成时刻 done[w]);1 = 正在取箱
        int[] act = new int[k];
        long[] done = new long[k];
        // 桥状态:busy 表示桥正被占用,busyUntil 为释放时刻
        boolean busy = false;
        long busyUntil = 0;
        long arrived = 0;     // 已到达左岸的箱子数
        long picked = 0;      // 已派出取箱的总次数
        long t = 0;
        // 效率:越大越优先过桥
        long[] eff = new long[k];
        for (int i = 0; i < k; i++) eff[i] = (long) time[i][0] + time[i][2];

        while (arrived < n) {
            // 同秒动作:结算完成的取/放箱
            for (int w = 0; w < k; w++) {
                if (act[w] == 0 && done[w] <= t) { act[w] = -1; side[w] = 0; } // 放完,回左岸等待
                if (act[w] == 1 && done[w] <= t) { act[w] = -1; side[w] = 1; } // 取完,回右岸等待
            }
            if (busy && busyUntil <= t) busy = false;
            // 若桥空闲,按规则选人:右侧优先(效率最低者),左其次
            if (!busy) {
                int bestRight = -1;
                for (int w = 0; w < k; w++) {
                    if (act[w] == -1 && side[w] == 1) {
                        if (bestRight == -1 || eff[w] > eff[bestRight]
                                || (eff[w] == eff[bestRight] && w > bestRight)) bestRight = w;
                    }
                }
                if (bestRight >= 0) {
                    int w = bestRight;
                    busy = true;
                    busyUntil = t + time[w][2]; // 右 -> 左过桥
                    act[w] = 0;
                    side[w] = 0;
                    done[w] = busyUntil + time[w][3]; // 放箱完成时刻
                    arrived++;                       // 一个箱子到达左岸
                    if (arrived == n) return (int) busyUntil; // 最后一个箱子桥左侧时刻
                } else if (picked < n) {
                    int bestLeft = -1;
                    for (int w = 0; w < k; w++) {
                        if (act[w] == -1 && side[w] == 0) {
                            if (bestLeft == -1 || eff[w] > eff[bestLeft]
                                    || (eff[w] == eff[bestLeft] && w > bestLeft)) bestLeft = w;
                        }
                    }
                    if (bestLeft >= 0) {
                        int w = bestLeft;
                        busy = true;
                        busyUntil = t + time[w][0]; // 左 -> 右过桥
                        picked++;
                        act[w] = 1;
                        side[w] = 1;
                        done[w] = busyUntil + time[w][1]; // 取箱完成时刻
                    }
                }
            }
            t++;
        }
        return (int) t;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        int r1 = s.findCrossingTime(1, 3, new int[][]{{1,1,2,1},{1,1,3,1},{1,1,4,1}});
        System.out.println(r1 + (r1 == 6 ? " PASS" : " FAIL expect 6"));
        int r2 = s.findCrossingTime(3, 2, new int[][]{{1,5,1,8},{10,10,10,10}});
        System.out.println(r2 + (r2 == 37 ? " PASS" : " FAIL expect 37"));

        // 随机对拍
        java.util.Random rnd = new java.util.Random(3);
        boolean ok = true;
        for (int t = 0; t < 1000; t++) {
            int k = 1 + rnd.nextInt(4);
            int n = 1 + rnd.nextInt(5);
            int[][] time = new int[k][4];
            for (int i = 0; i < k; i++) {
                for (int j = 0; j < 4; j++) time[i][j] = 1 + rnd.nextInt(4);
            }
            int r1x = s.findCrossingTime(n, k, time);
            int r2x = s.findCrossingTimeBrute(n, k, time);
            if (r1x != r2x) {
                ok = false;
                System.out.println("Mismatch n=" + n + " k=" + k);
                for (int i = 0; i < k; i++) System.out.println("  " + java.util.Arrays.toString(time[i]));
                System.out.println("  main=" + r1x + " brute=" + r2x);
                break;
            }
        }
        System.out.println(ok ? "2532 全部 PASS" : "2532 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O((n + k) log k);空间复杂度:O(k)。


2533. Number of Good Binary Strings(好二进制字符串的数量)

题目描述

给你四个整数 minLength、maxLength、oneGroup 和 zeroGroup。

好二进制字符串满足:

  • 长度在 `minLength`, `maxLength` 之间;
  • 每块连续 1 的个数是 oneGroup 的整数倍;
  • 每块连续 0 的个数是 zeroGroup 的整数倍(0 可以被认为是所有数字的倍数)。
    返回好二进制字符串的个数,答案对 10^9 + 7 取余。

示例 1

复制代码
minLength = 2, maxLength = 3, oneGroup = 1, zeroGroup = 2 -> 5

示例 2

复制代码
minLength = 4, maxLength = 4, oneGroup = 4, zeroGroup = 3 -> 1

提示:

  • 1 <= minLength <= maxLength <= 10^5;1 <= oneGroup, zeroGroup <= maxLength

解题思路

解法一:递推 / DP(主提交版本)

用 f0i 表示以 0 结尾、长度为 i 的好字符串个数,f1i 表示以 1 结尾的个数。

末尾一段连续 0 的长度必须是 zeroGroup 的整数倍且长度记为 t,

之前的位置长度为 i - t,且以 1 结尾(或 t == i 表示整串全是 0):

f0i = sum_{g>=1, t=gzeroGroup<=i} ( i-t==0 ? 1 : f1i-t )
f1i = sum_{g>=1, t=g
oneGroup<=i} ( i-t==0 ? 1 : f0i-t )

令 s0i = f00+f01+...+f0i,则 f0i = s1i-zeroGroup + s1i-2*zeroGroup + ...
其中 s1j = 1 + f11 + ... + f1j(s10 = 1 表示空串前缀以 0 结尾视为合法)。
为 O(1) 更新,我们维护「前缀和辅助」:每次递推 f0i 时需要 f1 下标 j = i - g* zeroGroup

处的累计。简单实现:前缀和 s1j = (累计 f1 到 j) + 1(把 j=0 空串计数为 1)。

这样 f0i = sum over valid t of prefix1i - t,遍历 g 从 1 开始逐段累加,

由于段长是 oneGroup/zeroGroup 的倍数,最多 i/group 项,最坏 O(N^2/group)。

优化:令 fs1i 表示所有下标对 group 取模为 i % group 的 s1 之和(滚动)。

实际上 f0i 只取决于和 i 同余(模 zeroGroup)的 s1 前缀累计。

记 Ai = Ai-zeroGroup + s1i-zeroGroup,那么 f0i 可由 i-zeroGroup 的累计递推:

f0i = Ai - zeroGroup,其中 Ai = Ai-zeroGroup + s1i。

同理 f1i = Bi - oneGroup,Bi = Bi-oneGroup + s0i。

其中 s0i = n0i(f0 前缀和)+ 1(空串),s1i = n1i + 1。

最终答案 = sum_{i=minLength}^{maxLength} (f0i + f1i)。

解法二:带记忆化搜索 / 直接 01 枚举,对照

按长度 i 枚举首个块是 0 还是 1,

块长必须是 group 的整数倍,暴力递归统计,仅用于小数据对拍。

时间复杂度:解法一 O(maxLength);空间复杂度:O(maxLength)。

代码实现

java 复制代码
public class Solution {

    private static final int MOD = 1_000_000_007;

    public int goodBinaryStrings(int minLength, int maxLength, int oneGroup, int zeroGroup) {
        // f0[i] 以 0 结尾的好字符串个数;f1[i] 以 1 结尾的好字符串个数
        long[] f0 = new long[maxLength + 1];
        long[] f1 = new long[maxLength + 1];
        // P1[i] = sum_{j ≡ i (mod zeroGroup), 1 <= j <= i-1} f1[j]
        // P0[i] = sum_{j ≡ i (mod oneGroup),  1 <= j <= i-1} f0[j]
        long[] P1 = new long[maxLength + 1];
        long[] P0 = new long[maxLength + 1];
        long ans = 0;
        for (int i = 1; i <= maxLength; i++) {
            if (i >= zeroGroup) P1[i] = (P1[i - zeroGroup] + f1[i - zeroGroup]) % MOD;
            if (i >= oneGroup) P0[i] = (P0[i - oneGroup] + f0[i - oneGroup]) % MOD;
            f0[i] = ((i % zeroGroup == 0 ? 1L : 0L) + P1[i]) % MOD;
            f1[i] = ((i % oneGroup == 0 ? 1L : 0L) + P0[i]) % MOD;
            if (i >= minLength) ans = (ans + f0[i] + f1[i]) % MOD;
        }
        return (int) ans;
    }

    public int goodBinaryStringsBrute(int minLength, int maxLength, int oneGroup, int zeroGroup) {
        int cnt = 0;
        for (int len = minLength; len <= maxLength; len++) {
            int total = 1 << len;
            for (int mask = 0; mask < total; mask++) {
                if (isGood(mask, len, oneGroup, zeroGroup)) cnt++;
            }
        }
        return cnt;
    }

    private boolean isGood(int mask, int len, int og, int zg) {
        int run = 0; // 0 = 尚未开始;正 = 当前连续 1 的个数;负 = 当前连续 0 的个数
        for (int i = 0; i < len; i++) {
            int bit = (mask >> (len - 1 - i)) & 1;
            if (bit == 1) {
                if (run < 0) { // 结束一段 0
                    if ((-run) % zg != 0) return false;
                    run = 1;
                } else {
                    run = (run > 0 ? run + 1 : 1);
                }
            } else {
                if (run > 0) { // 结束一段 1
                    if (run % og != 0) return false;
                    run = -1;
                } else {
                    run = (run < 0 ? run - 1 : -1);
                }
            }
        }
        if (run > 0 && run % og != 0) return false;
        if (run < 0 && (-run) % zg != 0) return false;
        return true;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.goodBinaryStrings(2, 3, 1, 2) == 5 ? "PASS" : "FAIL");
        System.out.println(s.goodBinaryStrings(4, 4, 4, 3) == 1 ? "PASS" : "FAIL");

        // 随机对拍(小数据暴力验证)
        java.util.Random rnd = new java.util.Random(4);
        boolean ok = true;
        for (int t = 0; t < 500; t++) {
            int minL = 1 + rnd.nextInt(8);
            int maxL = minL + rnd.nextInt(6);
            int og = 1 + rnd.nextInt(5);
            int zg = 1 + rnd.nextInt(5);
            int r1 = s.goodBinaryStrings(minL, maxL, og, zg);
            int r2 = s.goodBinaryStringsBrute(minL, maxL, og, zg);
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch minL=" + minL + " maxL=" + maxL
                        + " og=" + og + " zg=" + zg + " main=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2533 全部 PASS" : "2533 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O(maxLength);空间复杂度:O(maxLength)。


2534. Time Taken to Cross the Door(通过门的时间)

题目描述

n 个人按 0 到 n-1 编号,有一扇门,每个人只能通过门一次,耗时一秒。

给你非递减的 arrival 数组(arrival[i] 是第 i 个人到达门前的时间)和 state 数组

(state[i] = 0 表示想进入,1 表示想离开)。

若同时有多人想用门,规则:

  • 前一秒没有使用门 -> 想离开的人先离开;
  • 前一秒是进入 -> 想进入的人先进入;
  • 前一秒是离开 -> 想离开的人先离开;
  • 同方向多人 -> 编号最小者先。
    返回数组 answer,answer[i] 是第 i 个人通过门的时刻。

示例 1

复制代码
arrival = [0,1,1,2,4], state = [0,1,0,0,1] -> [0,3,1,2,4]

提示:

  • n == arrival.length == state.length,1 <= n <= 10^5;
  • 0 <= arrival[i] <= 10^5,arrival 非递减;state[i] ∈ {0,1}

解题思路

解法一:双队列模拟(主提交版本)

用数组排队:把当前时刻及之前到达、尚未进/出门的人按 state 分别放入两个队列

(队列里是同方向的人,按到达顺序即编号顺序排队)。

每秒钟:若上一秒有人进/出门,优先放行与上秒同向的队列(若空则放逆向);

否则(上秒无人用门)优先放行「离开」队列(若空则放「进入」队列)。

优先队列为空时,时间快进到下一个 arrival。

解法二:事件表暴力模拟,对照

逐秒推进,每次扫描所有未处理人员,选择当前符合条件的

最优者(方向优先级 + 最小编号),小规模对拍用。

时间复杂度:解法一 O(n);空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayDeque;

public class Solution {

    public int[] timeTaken(int[] arrival, int[] state) {
        int n = arrival.length;
        int[] ans = new int[n];
        ArrayDeque<Integer> enter = new ArrayDeque<Integer>(); // state=0
        ArrayDeque<Integer> exit = new ArrayDeque<Integer>();  // state=1
        int t = 0;     // 当前秒
        int idx = 0;   // 下一个到达的人
        int lastDir = -1; // 上一秒使用门的方向,-1 表示没有人用过

        while (idx < n || !enter.isEmpty() || !exit.isEmpty()) {
            // 把 t 时刻及之前到达的人入队
            while (idx < n && arrival[idx] <= t) {
                if (state[idx] == 0) enter.offer(idx);
                else exit.offer(idx);
                idx++;
            }
            int dir = -1;
            if (lastDir == 0) {          // 上一秒进入
                if (!enter.isEmpty()) dir = 0;
                else if (!exit.isEmpty()) dir = 1;
            } else if (lastDir == 1) {   // 上一秒离开
                if (!exit.isEmpty()) dir = 1;
                else if (!enter.isEmpty()) dir = 0;
            } else {                     // 上秒无人用门
                if (!exit.isEmpty()) dir = 1;
                else if (!enter.isEmpty()) dir = 0;
            }
            if (dir == -1) {
                // 本秒无人可用:快进到下一个人的到达时刻
                if (idx < n) t = arrival[idx];
                lastDir = -1; // 空档期间无人用门,重置记忆
                continue;
            }
            int person = (dir == 0) ? enter.poll() : exit.poll();
            ans[person] = t;
            lastDir = dir;
            t++;
        }
        return ans;
    }

    public int[] timeTakenBrute(int[] arrival, int[] state) {
        int n = arrival.length;
        int[] ans = new int[n];
        boolean[] done = new boolean[n];
        int t = 0;
        int lastDir = -1;
        int left = n;
        while (left > 0) {
            // 选择:可到的人里先按方向优先级,再按编号最小
            int dir = -1;
            int person = -1;
            // 确定方向优先级
            int pref = -1;
            if (lastDir == -1) pref = 1;
            else pref = lastDir;
            // 第一遍按 pref
            for (int cand = 0; cand < n; cand++) {
                if (!done[cand] && arrival[cand] <= t && state[cand] == pref) {
                    dir = pref;
                    person = cand;
                    break;
                }
            }
            if (person == -1) {
                for (int cand = 0; cand < n; cand++) {
                    if (!done[cand] && arrival[cand] <= t) {
                        dir = state[cand];
                        person = cand;
                        break;
                    }
                }
            }
            if (person == -1) {
                // 无人到:快进
                int nt = Integer.MAX_VALUE;
                for (int i = 0; i < n; i++) {
                    if (!done[i] && arrival[i] > t) nt = Math.min(nt, arrival[i]);
                }
                t = Math.max(t + 1, nt);
                lastDir = -1;
                continue;
            }
            ans[person] = t;
            done[person] = true;
            lastDir = dir;
            t++;
            left--;
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        int[] r = s.timeTaken(new int[]{0, 1, 1, 2, 4}, new int[]{0, 1, 0, 0, 1});
        System.out.println(java.util.Arrays.toString(r) + (java.util.Arrays.equals(r, new int[]{0, 3, 1, 2, 4}) ? " PASS" : " FAIL"));

        // 随机对拍
        java.util.Random rnd = new java.util.Random(5);
        boolean ok = true;
        for (int t = 0; t < 2000; t++) {
            int n = 1 + rnd.nextInt(8);
            int[] arr = new int[n];
            int[] st = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = (i == 0) ? rnd.nextInt(3) : arr[i - 1] + rnd.nextInt(3);
                st[i] = rnd.nextInt(2);
            }
            int[] r1 = s.timeTaken(arr, st);
            int[] r2 = s.timeTakenBrute(arr, st);
            if (!java.util.Arrays.equals(r1, r2)) {
                ok = false;
                System.out.println("Mismatch arr=" + java.util.Arrays.toString(arr)
                        + " st=" + java.util.Arrays.toString(st)
                        + " main=" + java.util.Arrays.toString(r1)
                        + " brute=" + java.util.Arrays.toString(r2));
                break;
            }
        }
        System.out.println(ok ? "2534 全部 PASS" : "2534 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O(n);空间复杂度:O(n)。


2535. Difference Between Element Sum and Digit Sum of an Array(数组元素和与数字和的绝对差)

题目描述

给你一个正整数数组 nums。

元素和是 nums 中的所有元素相加求和;数字和是 nums 中每一个元素的每一数位

(重复数位需多次求和)相加求和。返回元素和与数字和的绝对差。

示例 1

复制代码
nums = [1,15,6,3] -> 9

示例 2

复制代码
nums = [1,2,3,4] -> 0

提示:

  • 1 <= nums.length <= 2000;1 <= nums[i] <= 2000

解题思路

解法一:一次遍历(主提交版本)

对每个数累加其本身到元素和,循环除以 10 取余累加各位数字到数字和,

最后返回 |元素和 - 数字和|。

解法二:字符串写法,对照

将每个数转成字符串逐位求和,逻辑等价、便于交叉验证。

时间复杂度:O(n log max(numsi));空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {

    public int differenceOfSum(int[] nums) {
        int eleSum = 0;
        int digitSum = 0;
        for (int x : nums) {
            eleSum += x;
            int v = x;
            while (v > 0) {
                digitSum += v % 10;
                v /= 10;
            }
        }
        return Math.abs(eleSum - digitSum);
    }

    public int differenceOfSumStr(int[] nums) {
        int eleSum = 0;
        int digitSum = 0;
        for (int x : nums) {
            eleSum += x;
            String s = String.valueOf(x);
            for (int i = 0; i < s.length(); i++) {
                digitSum += s.charAt(i) - '0';
            }
        }
        return Math.abs(eleSum - digitSum);
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.differenceOfSum(new int[]{1, 15, 6, 3}) == 9 ? "PASS" : "FAIL");
        System.out.println(s.differenceOfSum(new int[]{1, 2, 3, 4}) == 0 ? "PASS" : "FAIL");

        // 随机对拍
        java.util.Random rnd = new java.util.Random(6);
        boolean ok = true;
        for (int t = 0; t < 2000; t++) {
            int n = 1 + rnd.nextInt(20);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) a[i] = 1 + rnd.nextInt(100000);
            if (s.differenceOfSum(a) != s.differenceOfSumStr(a)) {
                ok = false;
                System.out.println("Mismatch: " + java.util.Arrays.toString(a));
                break;
            }
        }
        System.out.println(ok ? "2535 全部 PASS" : "2535 存在不一致");
    }
}

复杂度分析

时间复杂度:O(n log max(numsi));空间复杂度:O(1)。


2536. Increment Submatrices by One(子矩阵元素加 1)

题目描述

给你一个正整数 n,表示最初有一个 n x n、下标从 0 开始的整数矩阵 mat,矩阵中填满了 0。

另给你一个二维整数数组 query,针对每个查询 query[i] = `row1_i`, `col1_i`, `row2_i`, `col2_i`,

将左上角 (row1_i, col1_i)、右下角 (row2_i, col2_i) 的子矩阵中每个元素加 1。

返回执行完所有操作后得到的矩阵。

示例 1

复制代码
n = 3, queries = [[1,1,2,2],[0,0,1,1]] -> [[1,1,0],[1,2,1],[0,1,1]]

示例 2

复制代码
n = 2, queries = [[0,0,1,1]] -> [[1,1],[1,1]]

提示:

  • 1 <= n <= 500;1 <= queries.length <= 10^4;
  • 0 <= row1_i <= row2_i < n;0 <= col1_i <= col2_i < n

解题思路

解法一:二维差分(主提交版本)

对每个矩形区域做差分:diffr1c1++, diffr2+1c1--,

diffr1c2+1--, diffr2+1c2+1++;最后对 diff 做两次前缀和还原矩阵,

每个 query 只需 O(1) 时间。

解法二:暴力逐格加 1,对照

直接对每个查询的四重循环累加,小数据下输出相同。

时间复杂度:解法一 O(n^2 + q);空间复杂度:O(n^2)。

代码实现

java 复制代码
public class Solution {

    public int[][] rangeAddQueries(int n, int[][] queries) {
        int[][] diff = new int[n + 1][n + 1];
        for (int[] q : queries) {
            int r1 = q[0], c1 = q[1], r2 = q[2], c2 = q[3];
            diff[r1][c1]++;
            diff[r2 + 1][c1]--;
            diff[r1][c2 + 1]--;
            diff[r2 + 1][c2 + 1]++;
        }
        int[][] mat = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int v = diff[i][j];
                if (i > 0) v += mat[i - 1][j];
                if (j > 0) v += mat[i][j - 1];
                if (i > 0 && j > 0) v -= mat[i - 1][j - 1];
                mat[i][j] = v;
            }
        }
        return mat;
    }

    public int[][] rangeAddQueriesBrute(int n, int[][] queries) {
        int[][] mat = new int[n][n];
        for (int[] q : queries) {
            for (int i = q[0]; i <= q[2]; i++) {
                for (int j = q[1]; j <= q[3]; j++) {
                    mat[i][j]++;
                }
            }
        }
        return mat;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        int[][] r1 = s.rangeAddQueries(3, new int[][]{{1, 1, 2, 2}, {0, 0, 1, 1}});
        System.out.println(java.util.Arrays.deepToString(r1));
        System.out.println("示例1 " + (java.util.Arrays.deepEquals(r1, new int[][]{{1, 1, 0}, {1, 2, 1}, {0, 1, 1}}) ? "PASS" : "FAIL"));
        int[][] r2 = s.rangeAddQueries(2, new int[][]{{0, 0, 1, 1}});
        System.out.println("示例2 " + (java.util.Arrays.deepEquals(r2, new int[][]{{1, 1}, {1, 1}}) ? "PASS" : "FAIL"));

        // 随机对拍
        java.util.Random rnd = new java.util.Random(7);
        boolean ok = true;
        for (int t = 0; t < 1000; t++) {
            int n = 1 + rnd.nextInt(7);
            int q = 1 + rnd.nextInt(10);
            int[][] qs = new int[q][4];
            for (int[] qu : qs) {
                int a = rnd.nextInt(n), b = rnd.nextInt(n);
                int c = a + rnd.nextInt(n - a), d = b + rnd.nextInt(n - b);
                qu[0] = a; qu[1] = b; qu[2] = c; qu[3] = d;
            }
            int[][] m1 = s.rangeAddQueries(n, qs);
            int[][] m2 = s.rangeAddQueriesBrute(n, qs);
            if (!java.util.Arrays.deepEquals(m1, m2)) {
                ok = false;
                System.out.println("Mismatch n=" + n + " qs=" + java.util.Arrays.deepToString(qs));
                System.out.println("main=" + java.util.Arrays.deepToString(m1));
                System.out.println("brute=" + java.util.Arrays.deepToString(m2));
                break;
            }
        }
        System.out.println(ok ? "2536 全部 PASS" : "2536 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O(n^2 + q);空间复杂度:O(n^2)。


2537. Count the Number of Good Subarrays(统计好子数组的数目)

题目描述

给你一个整数数组 nums 和一个整数 k,返回 nums 中「好」子数组的数目。

一个子数组 arr 如果有至少 k 对下标 (i, j) 满足 i < j 且 arr[i] == arr[j],

那么称它是一个好子数组。子数组是原数组中一段连续非空的元素序列。

示例 1

复制代码
nums = [1,1,1,1,1], k = 10 -> 1

示例 2

复制代码
nums = [3,1,4,3,2,2,4], k = 2 -> 4

提示:

  • 1 <= nums.length <= 10^5;1 <= nums[i], k <= 10^9

解题思路

解法一:滑动窗口(主提交版本)

窗口内相同的数对数为 pairs = Σ C(freqv, 2)。右端 r 每扩展一格,

若 numsr 之前已出现 freq 次,则新增 freq 对,pairs += freq。

当 pairs >= k 时,对固定的左端 l,右端取 r...n-1 的任意值都满足条件,

一次累加 n - r 个,然后收缩左端 l(移出元素后 pairs 减少 freq-1 对)。

复杂度 O(n)。

解法二:暴力枚举,对照

枚举所有子数组,统计每对相等下标,O(n^2) 仅用于小数据对拍。

时间复杂度:解法一 O(n);空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.HashMap;

public class Solution {

    public long countGood(int[] nums, int k) {
        int n = nums.length;
        HashMap<Integer, Integer> freq = new HashMap<Integer, Integer>();
        long pairs = 0;
        long ans = 0;
        int l = 0;
        for (int r = 0; r < n; r++) {
            int f = freq.getOrDefault(nums[r], 0);
            pairs += f;          // 与前面 f 个相同元素各组成一对
            freq.put(nums[r], f + 1);
            while (pairs >= k) {
                ans += n - r;    // 所有以 l 开头、右端点 >= r 的子数组均满足
                int fl = freq.get(nums[l]);
                pairs -= fl - 1; // 移除 nums[l] 后减少 fl-1 对
                freq.put(nums[l], fl - 1);
                l++;
            }
        }
        return ans;
    }

    public long countGoodBrute(int[] nums, int k) {
        int n = nums.length;
        long ans = 0;
        for (int i = 0; i < n; i++) {
            HashMap<Integer, Integer> cnt = new HashMap<Integer, Integer>();
            long pairs = 0;
            for (int j = i; j < n; j++) {
                int f = cnt.getOrDefault(nums[j], 0);
                pairs += f;
                cnt.put(nums[j], f + 1);
                if (pairs >= k) ans++;
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.countGood(new int[]{1, 1, 1, 1, 1}, 10) == 1 ? "PASS" : "FAIL");
        System.out.println(s.countGood(new int[]{3, 1, 4, 3, 2, 2, 4}, 2) == 4 ? "PASS" : "FAIL");

        // 随机对拍
        java.util.Random rnd = new java.util.Random(8);
        boolean ok = true;
        for (int t = 0; t < 1000; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) a[i] = rnd.nextInt(6);
            int k = 1 + rnd.nextInt(15);
            long r1 = s.countGood(a, k);
            long r2 = s.countGoodBrute(a, k);
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch arr=" + java.util.Arrays.toString(a)
                        + " k=" + k + " main=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2537 全部 PASS" : "2537 存在不一致");
    }
}

复杂度分析

时间复杂度:解法一 O(n);空间复杂度:O(n)。


2538. Difference Between Maximum and Minimum Price Sum(最大价值和与最小价值和的差值)

题目描述

给你一个 n 个节点的无向无根树,节点编号 0 到 n-1,edges 表示树边。

每个节点都有价值 price[i]。一条路径的价值和是路径上所有节点价值之和。

你可以选择树中任意一个节点作为根节点 root。选择 root 为根的开销是:

以 root 为起点的所有路径中,价值和最大的一条路径与最小的一条路径的差值。

返回所有节点作为根的选择中,最大的开销。

示例 1

复制代码
n = 6, edges = [[0,1],[1,2],[1,3],[3,4],[3,5]], price = [9,8,7,6,10,5] -> 24

示例 2

复制代码
n = 3, edges = [[0,1],[1,2]], price = [1,1,1] -> 2

提示:

  • 1 <= n <= 10^5;edges.length == n-1;1 <= price[i] <= 10^5

解题思路

解法一:树形 DP / 树的直径(主提交版本)

开销 = 以 root 为端点的一条「最大权和路径」减去「单节点最小路径」(只含 root 自己)。

因为最小路径显然可以取只含根节点的路径(价值 priceroot),而最大路径任意。

所以问题等价于:求树上以任意节点为端点的带权路径(点权和)的最大值,再减去该端点价值,

再对所有端点取最大 ------ 也就是求树的带权直径 D,答案 = D - min_price 吗?

不对:最小路径必须是以 root 为起点(即 root 是端点的路径),且最小路径取自 root 的单点。

因此开销 = (以 root 为一端的最大路径价值和) - priceroot。

记 downu = 从 u 向下(子树中)出发的最大路径价值和(包含 u)。

以 u 为根(假设树的根固定为 0,但任意节点均可做为路径端点),

「以 u 为端点的最大路径和」= max(down 子节点 + priceu),即从 u 走到子树最深。

而整棵树中「以 u 为一端」的最大值还要考虑向上走,这里通过换根 DP:

upu = 从 u 向上走能获得的最大路径和(包含 u)。

最终 ans = max over u of (max(downPath(u), upPath(u)) - priceu)。

其中 downPath(u) = max(priceu, maxChild(downc + priceu)),

upPath(u) = priceu + max(0, upPath(parent-去child方向))。

实现上:第一次 dfs 计算 downu;第二次 dfs 换根,upchild =

pricechild + max(0, max(upu - priceu必含u, downsib - priceu... )) 实际:

从 u 到 child 方向之外的「走出 u 的最大路径」bestOut =

max(priceu 单点, upu(从 u 向上), max over other children c' of downc'),

upchild = bestOut + pricechild。

其中 upu 表示「从 u 向上(父方向)走的最大路径和,包含 u」。

解法二:暴力对拍

小数据下枚举每个根,跑 DFS 求所有路径价值和的 max 与 min。

时间复杂度:O(n);空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Solution {

    public long maxOutput(int n, int[][] edges, int[] price) {
        List<Integer>[] g = new List[n];
        for (int i = 0; i < n; i++) g[i] = new ArrayList<Integer>();
        for (int[] e : edges) {
            g[e[0]].add(e[1]);
            g[e[1]].add(e[0]);
        }
        long[] down = new long[n]; // 以 0 为根,向下走的最大路径和(含自身)
        dfsDown(0, -1, g, price, down);
        // 换根求 up:up[u] = 从 u 向父方向走的最大路径和(含 u)
        long[] up = new long[n];
        dfsUp(0, -1, g, price, down, up);
        long ans = 0;
        for (int u = 0; u < n; u++) {
            long best = Math.max(down[u], up[u]);
            ans = Math.max(ans, best - price[u]);
        }
        return ans;
    }

    private void dfsDown(int u, int p, List<Integer>[] g, int[] price, long[] down) {
        long best = price[u];
        for (int v : g[u]) {
            if (v == p) continue;
            dfsDown(v, u, g, price, down);
            best = Math.max(best, down[v] + price[u]);
        }
        down[u] = best;
    }

    private void dfsUp(int u, int p, List<Integer>[] g, int[] price, long[] down, long[] up) {
        // 先处理 p 的方向(向上)+ 兄弟子树的 bestOut
        for (int v : g[u]) {
            if (v == p) continue;
            // 从 u 出发不经过 v 所能得到的最大路径和(包含 u)
            long bestOut = price[u];
            if (p != -1) bestOut = Math.max(bestOut, up[u]);
            for (int c : g[u]) {
                if (c == p || c == v) continue;
                bestOut = Math.max(bestOut, down[c] + price[u]);
            }
            up[v] = bestOut + price[v];
            dfsUp(v, u, g, price, down, up);
        }
    }

    public long maxOutputBrute(int n, int[][] edges, int[] price) {
        List<Integer>[] g = new List[n];
        for (int i = 0; i < n; i++) g[i] = new ArrayList<Integer>();
        for (int[] e : edges) {
            g[e[0]].add(e[1]);
            g[e[1]].add(e[0]);
        }
        long ans = 0;
        // 对每个 root,DFS 它的所有路径(枚举路径另一端),路径和 min 一定取 root 单点,
        // 因此只需求以 root 为端点的最大路径和。
        for (int root = 0; root < n; root++) {
            long maxPath = maxPathFrom(root, -1, g, price);
            ans = Math.max(ans, maxPath - price[root]);
        }
        return ans;
    }

    private long maxPathFrom(int u, int p, List<Integer>[] g, int[] price) {
        long best = price[u];
        for (int v : g[u]) {
            if (v == p) continue;
            best = Math.max(best, maxPathFrom(v, u, g, price) + price[u]);
        }
        return best;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.maxOutput(6, new int[][]{{0,1},{1,2},{1,3},{3,4},{3,5}}, new int[]{9,8,7,6,10,5}) == 24 ? "PASS" : "FAIL");
        System.out.println(s.maxOutput(3, new int[][]{{0,1},{1,2}}, new int[]{1,1,1}) == 2 ? "PASS" : "FAIL");

        // 随机对拍
        java.util.Random rnd = new java.util.Random(9);
        boolean ok = true;
        for (int t = 0; t < 800; t++) {
            int n = 2 + rnd.nextInt(8);
            int[][] edges = new int[n - 1][2];
            for (int i = 1; i < n; i++) {
                edges[i - 1][0] = rnd.nextInt(i);
                edges[i - 1][1] = i;
            }
            int[] price = new int[n];
            for (int i = 0; i < n; i++) price[i] = 1 + rnd.nextInt(9);
            long r1 = s.maxOutput(n, edges, price);
            long r2 = s.maxOutputBrute(n, edges, price);
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch n=" + n + " edges=" + java.util.Arrays.deepToString(edges)
                        + " price=" + java.util.Arrays.toString(price) + " main=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2538 全部 PASS" : "2538 存在不一致");
    }
}

复杂度分析

时间复杂度:O(n);空间复杂度:O(n)。


2539. Count the Number of Good Subsequences(好子序列的个数)

题目描述

如果字符串的某个子序列不为空,且其中每一个字符出现的频率都相同,

就认为该子序列是一个好子序列。给定字符串 s,统计并返回它的好子序列的个数,

答案对 10^9 + 7 取余。

示例 1

复制代码
s = "aabb" -> 11

示例 2

复制代码
s = "leet" -> 12

示例 3

复制代码
s = "abcd" -> 15

提示:

  • 1 <= s.length <= 10^4;s 仅由小写英文字母组成

解题思路

解法一:组合计数(主提交版本)

统计每个字符的出现次数 cntc。一个好子序列中,每个被选中的字符都恰好出现 f 次

(f >= 1),未选中的字符出现 0 次。因此固定 f 时,选择方案数为

Π_c ( 1 + C(cntc, f) )

其中 cntc < f 时 C(cntc, f) = 0(该字符只能不选)。减去全都不选的 1 种。

总和 = Σ_{f=1}^{maxCnt} ( Π_c (1 + C(cntc, f)) - 1 )。

组合数用阶乘 + 逆元 O(1) 查询。

解法二:二进制枚举,对照

穷举 s 的所有子序列(下标子集),检查是否为好子序列,

仅用于小数据对拍。

时间复杂度:O(26 * maxCnt);空间复杂度:O(maxCnt)。

代码实现

java 复制代码
public class Solution {

    private static final int MOD = 1_000_000_007;

    public int countSubsequences(String s) {
        int[] cnt = new int[26];
        for (char ch : s.toCharArray()) cnt[ch - 'a']++;
        int maxCnt = 0;
        for (int c : cnt) maxCnt = Math.max(maxCnt, c);
        if (maxCnt == 0) return 0;

        // 预处理阶乘与逆元
        long[] fact = new long[maxCnt + 1];
        long[] invFact = new long[maxCnt + 1];
        fact[0] = 1;
        for (int i = 1; i <= maxCnt; i++) fact[i] = fact[i - 1] * i % MOD;
        invFact[maxCnt] = modPow(fact[maxCnt], MOD - 2);
        for (int i = maxCnt; i > 0; i--) invFact[i - 1] = invFact[i] * i % MOD;

        long ans = 0;
        for (int f = 1; f <= maxCnt; f++) {
            long ways = 1;
            for (int c = 0; c < 26; c++) {
                if (cnt[c] >= f) {
                    long comb = fact[cnt[c]] * invFact[f] % MOD * invFact[cnt[c] - f] % MOD;
                    ways = ways * (1 + comb) % MOD;
                }
            }
            ans = (ans + ways - 1) % MOD;
        }
        return (int) ((ans + MOD) % MOD);
    }

    private long modPow(long a, long e) {
        long r = 1;
        while (e > 0) {
            if ((e & 1) == 1) r = r * a % MOD;
            a = a * a % MOD;
            e >>= 1;
        }
        return r;
    }

    public int countSubsequencesBrute(String s) {
        int n = s.length();
        int[] cnt = new int[26];
        int good = 0;
        for (int mask = 1; mask < (1 << n); mask++) {
            java.util.Arrays.fill(cnt, 0);
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0) cnt[s.charAt(i) - 'a']++;
            }
            int f = -1;
            boolean ok = true;
            for (int c = 0; c < 26; c++) {
                if (cnt[c] > 0) {
                    if (f == -1) f = cnt[c];
                    else if (cnt[c] != f) { ok = false; break; }
                }
            }
            if (ok) good++;
        }
        return good;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.countSubsequences("aabb") == 11 ? "PASS" : "FAIL");
        System.out.println(s.countSubsequences("leet") == 12 ? "PASS" : "FAIL");
        System.out.println(s.countSubsequences("abcd") == 15 ? "PASS" : "FAIL");

        // 随机对拍(n <= 12)
        java.util.Random rnd = new java.util.Random(10);
        boolean ok = true;
        for (int t = 0; t < 500; t++) {
            StringBuilder sb = new StringBuilder();
            int n = 1 + rnd.nextInt(10);
            for (int i = 0; i < n; i++) sb.append((char) ('a' + rnd.nextInt(3)));
            String str = sb.toString();
            int r1 = s.countSubsequences(str);
            int r2 = s.countSubsequencesBrute(str);
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch s=" + str + " main=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2539 全部 PASS" : "2539 存在不一致");
    }
}

复杂度分析

时间复杂度:O(26 * maxCnt);空间复杂度:O(maxCnt)。


2540. Minimum Common Value(最小公共值)

题目描述

给你两个已按非降序排序的整数数组 nums1 和 nums2,返回两个数组的最小公共整数。

如果两个数组没有公共整数,返回 -1。

如果一个整数在两个数组中都至少出现一次,那么这个整数是公共的。

示例 1

复制代码
nums1 = [1,2,3], nums2 = [2,4] -> 2

示例 2

复制代码
nums1 = [1,2,3,6], nums2 = [2,3,4,5] -> 2

提示:

  • 1 <= nums1.length, nums2.length <= 10^5;1 <= nums1i, nums2j <= 10^9;
  • 两个数组均非降序。

解题思路

解法一:双指针(主提交版本)

两指针从 0 开始,比较当前值,相等即答案;

小者指针后移。时间 O(n+m)。

解法二:二分查找,对照

遍历 nums1,对每个元素在 nums2 中二分查找。

时间 O(n log m)。

时间复杂度:O(n+m);空间复杂度:O(1)。

代码实现

java 复制代码
import java.util.Arrays;

public class Solution {

    public int getCommon(int[] nums1, int[] nums2) {
        int i = 0, j = 0;
        while (i < nums1.length && j < nums2.length) {
            if (nums1[i] == nums2[j]) return nums1[i];
            if (nums1[i] < nums2[j]) i++;
            else j++;
        }
        return -1;
    }

    public int getCommonBinary(int[] nums1, int[] nums2) {
        for (int x : nums1) {
            int idx = Arrays.binarySearch(nums2, x);
            if (idx >= 0) return x;
        }
        return -1;
    }

    public static void main(String[] args) {
        Solution s = new Solution();

        // 示例测试
        System.out.println(s.getCommon(new int[]{1, 2, 3}, new int[]{2, 4}) == 2 ? "PASS" : "FAIL");
        System.out.println(s.getCommon(new int[]{1, 2, 3, 6}, new int[]{2, 3, 4, 5}) == 2 ? "PASS" : "FAIL");

        // 随机对拍
        java.util.Random rnd = new java.util.Random(11);
        boolean ok = true;
        for (int t = 0; t < 2000; t++) {
            int n = 1 + rnd.nextInt(15);
            int m = 1 + rnd.nextInt(15);
            int[] a = new int[n];
            int[] b = new int[m];
            for (int i = 0; i < n; i++) a[i] = rnd.nextInt(20);
            for (int i = 0; i < m; i++) b[i] = rnd.nextInt(20);
            Arrays.sort(a);
            Arrays.sort(b);
            int r1 = s.getCommon(a, b);
            int r2 = s.getCommonBinary(a, b);
            if (r1 != r2) {
                ok = false;
                System.out.println("Mismatch a=" + Arrays.toString(a) + " b=" + Arrays.toString(b)
                        + " main=" + r1 + " brute=" + r2);
                break;
            }
        }
        System.out.println(ok ? "2540 全部 PASS" : "2540 存在不一致");
    }
}

复杂度分析

小者指针后移。时间 O(n+m)。

时间 O(n log m)。

时间复杂度:O(n+m);空间复杂度:O(1)。

相关推荐
杨凯凡12 分钟前
Spring Framework 7 HTTP Interface 实战:声明式 RestClient、超时与测试
java·spring·http
吴名氏.28 分钟前
Java转AI Agent教程(第十二周)检索与切分
java·开发语言·人工智能·ai·ai agent
瑞雪兆丰年兮1 小时前
JavaWeb-day10-SpringBootWeb案例
java·开发语言·javaweb·web
Rain的Java大神之路1 小时前
你背的Spring答案,全是5.x时代的:7.0.8源码一翻,80%的博客都过时了
java·后端·面试
这个昵称不容易2 小时前
考试报名系统的设计与实现(SpringBoot + MySQL + Vue 三角色平台)
java·springboot·毕设
Adellle2 小时前
设计模式指南 (Java 版) ㊁
java·开发语言·设计模式
特立独行的猫A3 小时前
cj-tauri:用仓颉写桌面与鸿蒙 PC 应用
java·后端·python
Lonely丶墨轩3 小时前
从一个 404 到负数 ID:第三方接口开发中的几次排错
java
聪明的笨猪猪3 小时前
实际工程问题:AI生成MyBatis批量操作的SQL超限与性能雪崩
java·经验分享·面试·ai编程