3524.求出数组的X值I

目标

给你一个由 正 整数组成的数组 nums,以及一个 正 整数 k。

你可以对 nums 执行 一次 操作,该操作中可以移除任意 不重叠 的前缀和后缀,使得 nums 仍然 非空 。

你需要找出 nums 的 x 值,即在执行操作后,剩余元素的 乘积 除以 k 后的 余数 为 x 的操作数量。

返回一个大小为 k 的数组 result,其中 result[x] 表示对于 0 <= x <= k - 1,nums 的 x 值。

数组的 前缀 指从数组起始位置开始到数组中任意位置的一段连续子数组。

数组的 后缀 是指从数组中任意位置开始到数组末尾的一段连续子数组。

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

注意,在操作中选择的前缀和后缀可以是 空的 。

示例 1:

输入: nums = [1,2,3,4,5], k = 3
输出: [9,2,4]
解释:
对于 x = 0,可行的操作包括所有不会移除 nums[2] == 3 的前后缀移除方式。
对于 x = 1,可行操作包括:
    移除空前缀和后缀 [2, 3, 4, 5],nums 变为 [1]。
    移除前缀 [1, 2, 3] 和后缀 [5],nums 变为 [4]。
对于 x = 2,可行操作包括:
    移除空前缀和后缀 [3, 4, 5],nums 变为 [1, 2]。
    移除前缀 [1] 和后缀 [3, 4, 5],nums 变为 [2]。
    移除前缀 [1, 2, 3] 和空后缀,nums 变为 [4, 5]。
    移除前缀 [1, 2, 3, 4] 和空后缀,nums 变为 [5]。

示例 2:

输入: nums = [1,2,4,8,16,32], k = 4
输出: [18,1,2,0]
解释:
对于 x = 0,唯一 不 得到 x = 0 的操作有:
    移除空前缀和后缀 [4, 8, 16, 32],nums 变为 [1, 2]。
    移除空前缀和后缀 [2, 4, 8, 16, 32],nums 变为 [1]。
    移除前缀 [1] 和后缀 [4, 8, 16, 32],nums 变为 [2]。
对于 x = 1,唯一的操作是:
    移除空前缀和后缀 [2, 4, 8, 16, 32],nums 变为 [1]。
对于 x = 2,可行操作包括:
    移除空前缀和后缀 [4, 8, 16, 32],nums 变为 [1, 2]。
    移除前缀 [1] 和后缀 [4, 8, 16, 32],nums 变为 [2]。
对于 x = 3,没有可行的操作。

示例 3:

输入: nums = [1,1,2,1,1], k = 2
输出: [9,6]

说明:

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

思路

有一个正整数数组 nums 和一个正整数 k,每次操作可以移除数组不重叠的前缀与后缀,即保留中间非空的子数组。求子数组元素乘积模 k 的余数为 x 的操作数量。

定义 dp[i][j] 表示 [0, i] 中以 i 为右端点的子数组中模 kj 的个数。使用刷表法更新 dp[i][nums[i] % k]++, dp[i][nums[i] * j % k] += dp[i - 1][j]

代码


/**
 * @date 2026-09-21 15:19
 */
public class ResultArray3524 {

    public long[] resultArray(int[] nums, int k) {
        int n = nums.length;
        long[][] dp = new long[n][k];
        dp[0][nums[0] % k] = 1;
        long[] res = new long[k];
        res[nums[0] % k]++;
        for (int i = 1; i < n; i++) {
            int rem = nums[i] % k;
            dp[i][rem]++;
            for (int j = 0; j < k; j++) {
                dp[i][(int) ((long) nums[i] * j % k)] += dp[i - 1][j];
            }
            for (int j = 0; j < k; j++) {
                res[j] += dp[i][j];
            }
        }
        return res;
    }

}

性能

3498.字符串的反转度

目标

给你一个字符串 s,计算其 反转度。

反转度的计算方法如下:

  1. 对于每个字符,将其在 反转 字母表中的位置('a' = 26, 'b' = 25, ..., 'z' = 1)与其在字符串中的位置(下标从1 开始)相乘。
  2. 将这些乘积加起来,得到字符串中所有字符的和。

返回 反转度。

示例 1:

输入: s = "abc"
输出: 148
解释:
字母 反转字母表中的位置 字符串中的位置 乘积
'a'        26              1        26
'b'        25              2        50
'c'        24              3        72
反转度是 26 + 50 + 72 = 148 。

示例 2:

输入: s = "zaza"
输出: 160
解释:
字母 反转字母表中的位置 字符串中的位置 乘积
'z'        1             1         1
'a'        26            2        52
'z'        1             3         3
'a'        26            4        104
反转度是 1 + 52 + 3 + 104 = 160 。

说明:

  • 1 <= s.length <= 1000
  • s 仅包含小写字母。

思路

定义字符串中字符的反转度为每个字符的下标(从 1 开始)乘以该字符在反转字符表中的位置的乘积,字符串的反转度是其字符反转度之和。

依题意模拟即可。

代码


/**
 * @date 2026-09-21 16:49
 */
public class ReverseDegree3498 {

    public int reverseDegree(String s) {
        int res = 0;
        int n = s.length();
        for (int i = 1; i <= n; i++) {
            res += ('z' - s.charAt(i - 1) + 1) * i;
        }
        return res;
    }
}

性能

1477.找两个和为目标值且不重叠的子数组

目标

给你一个整数数组 arr 和一个整数值 target 。

请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target 。可能会有多种方案,请你返回满足要求的两个子数组长度和的 最小值 。

请返回满足要求的最小长度和,如果无法找到这样的两个子数组,请返回 -1 。

示例 1:

输入:arr = [3,2,2,4,3], target = 3
输出:2
解释:只有两个子数组和为 3 ([3] 和 [3])。它们的长度和为 2 。

示例 2:

输入:arr = [7,3,4,7], target = 7
输出:2
解释:尽管我们有 3 个互不重叠的子数组和为 7 ([7], [3,4] 和 [7]),但我们会选择第一个和第三个子数组,因为它们的长度和 2 是最小值。

示例 3:

输入:arr = [4,3,2,6,2,3,4], target = 6
输出:-1
解释:我们只有一个和为 6 的子数组。

示例 4:

输入:arr = [5,5,4,4,5], target = 3
输出:-1
解释:我们无法找到和为 3 的子数组。

示例 5:

输入:arr = [3,1,1,1,5,1,2,1], target = 3
输出:3
解释:注意子数组 [1,2] 和 [2,1] 不能成为一个方案因为它们重叠了。

说明:

  • 1 <= arr.length <= 10^5
  • 1 <= arr[i] <= 1000
  • 1 <= target <= 10^8

思路

有一个整数数组 arr 和一个整数值 target,从数组中找到两个不重叠的子数组,使得子数组的和为 target,返回这两个子数组长度之和的最小值。

将重叠问题进行前后缀分解,在各自的部分只需考虑和为 target 的最小长度,最小长度可以使用滑动窗口。

代码


/**
 * @date 2026-09-17 10:06
 */
public class MinSumOfLengths1477 {

    public int minSumOfLengths(int[] arr, int target) {
        int n = arr.length;
        int[] pre = new int[n + 1];
        int[] suf = new int[n + 1];
        Arrays.fill(pre, Integer.MAX_VALUE);
        Arrays.fill(suf, Integer.MAX_VALUE);
        int sum = 0;
        int l = 0;
        for (int i = 0; i < n; i++) {
            sum += arr[i];
            while (sum > target) {
                sum -= arr[l++];
            }
            if (sum == target) {
                pre[i + 1] = Math.min(pre[i], i - l + 1);
            } else {
                pre[i + 1] = pre[i];
            }
        }
        sum = 0;
        int r = n - 1;
        for (int i = n - 1; i >= 0; i--) {
            sum += arr[i];
            while (sum > target) {
                sum -= arr[r--];
            }
            if (sum == target) {
                suf[i] = Math.min(suf[i + 1], r - i + 1);
            } else {
                suf[i] = suf[i + 1];
            }
        }
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            if (pre[i + 1] != Integer.MAX_VALUE && suf[i + 1] != Integer.MAX_VALUE) {
                res = Math.min(res, pre[i + 1] + suf[i + 1]);
            }
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }

}

性能

2472.不重叠回文子字符串的最大数目

目标

给你一个字符串 s 和一个 正 整数 k 。

从字符串 s 中选出一组满足下述条件且 不重叠 的子字符串:

  • 每个子字符串的长度 至少 为 k 。
  • 每个子字符串是一个 回文串 。

返回最优方案中能选择的子字符串的 最大 数目。

子字符串 是字符串中一个连续的字符序列。

示例 1 :

输入:s = "abaccdbbd", k = 3
输出:2
解释:可以选择 s = "abaccdbbd" 中斜体加粗的子字符串。"aba" 和 "dbbd" 都是回文,且长度至少为 k = 3 。
可以证明,无法选出两个以上的有效子字符串。

示例 2 :

输入:s = "adbcda", k = 2
输出:0
解释:字符串中不存在长度至少为 2 的回文子字符串。

说明:

  • 1 <= k <= s.length <= 2000
  • s 仅由小写英文字母组成

思路

返回字符串 s 中长度 至少k 的不重叠回文子串的最大个数。

定义 dp[i] 表示 s[i, n - 1] 中长度为 k 的不重叠回文子串的最大数目,

  • 如果不选 s[i]dp[i] = dp[i + 1]
  • 如果选择 s[i],枚举终点 j,使得 j - i + 1 >= k,如果 s[i, j] 是回文,dp[i] = max(dp[i], 1 + dp[j + 1]))

需要快速判断子串是否是回文,可以预处理。

代码


/**
 * @date 2026-09-15 9:24
 */
public class MaxPalindromes2472 {

    public int maxPalindromes(String s, int k) {
        int n = s.length();
        boolean[][] isPalindrome = new boolean[n][n];
        for (int i = 0; i < n; i++) {
            isPalindrome[i][i] = true;
            int l = i - 1, r = i + 1;
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)){
                isPalindrome[l--][r++] = true;
            }
            l = i - 1;
            r = i;
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)){
                isPalindrome[l--][r++] = true;
            }
        }
        int[] dp = new int[n + 1];
        for (int i = n - k; i >= 0; i--) {
            dp[i] = dp[i + 1];
            for (int j = n - 1; j >= i + k - 1; j--) {
                if (isPalindrome[i][j]) {
                    dp[i] = Math.max(dp[i], 1 + dp[j + 1]);
                }
            }
        }
        return dp[0];
    }

}

性能

836.矩形重叠

目标

矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。矩形的上下边平行于 x 轴,左右边平行于 y 轴。

如果相交的面积为 正 ,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。

给出两个矩形 rec1 和 rec2 。如果它们重叠,返回 true;否则,返回 false 。

示例 1:

输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3]
输出:true

示例 2:

输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1]
输出:false

示例 3:

输入:rec1 = [0,0,1,1], rec2 = [2,2,3,3]
输出:false

说明:

  • rect1.length == 4
  • rect2.length == 4
  • -10^9 <= rec1[i], rec2[i] <= 10^9
  • rec1 和 rec2 表示一个面积不为零的有效矩形

思路

使用数组 [x1, y1, x2, y2] 表示一个矩形的 左下 (x1, y1) 与 右上 (x2, y2) 顶点。给出两个数组 rec1rec2,判断它们是否重叠。

如果两个矩形重叠,那么两个矩形在两个坐标轴方向的投影区间都应该相交。

代码


/**
 * @date 2026-09-14 9:08
 */
public class IsRectangleOverlap836 {

    public boolean isRectangleOverlap(int[] rec1, int[] rec2) {
        int x1 = rec1[0], x3 = rec2[0];
        int y1 = rec1[1], y3 = rec2[1];
        int x2 = rec1[2], x4 = rec2[2];
        int y2 = rec1[3], y4 = rec2[3];
        return !(x2 <= x3 || x4 <= x1) && !(y2 <= y3 || y4 <= y1);
    }

}

性能

3483.不同三位偶数的数目

目标

给你一个数字数组 digits,你需要从中选择三个数字组成一个三位偶数,你的任务是求出 不同 三位偶数的数量。

注意:每个数字在三位偶数中都只能使用 一次 ,并且 不能 有前导零。

示例 1:

输入: digits = [1,2,3,4]
输出: 12
解释: 可以形成的 12 个不同的三位偶数是 124,132,134,142,214,234,312,314,324,342,412 和 432。注意,不能形成 222,因为数字 2 只有一个。

示例 2:

输入: digits = [0,2,2]
输出: 2
解释: 可以形成的三位偶数是 202 和 220。注意,数字 2 可以使用两次,因为数组中有两个 2 。

示例 3:

输入: digits = [6,6,6]
输出: 1
解释: 只能形成 666。

示例 4:

输入: digits = [1,3,5]
输出: 0
解释: 无法形成三位偶数。

说明:

  • 3 <= digits.length <= 10
  • 0 <= digits[i] <= 9

思路

有一个数字数组 digits,数组元素为 0 ~ 9 ,从中选择 3 个组成一个三位偶数,求不同的三位偶数的个数。

从结果来考虑,三位数 100 ~ 999,针对每个数字判断能否由数组中的数字构成即可。

从构造的角度考虑,需要保证个位是偶数,且百位不是 0,要求数字不同,需要维护可用的数字种类,需要知道每个数字的已使用次数,可以使用回溯。

代码


/**
 * @date 2026-09-11 8:56
 */
public class TotalNumbers3483 {

    public int totalNumbers(int[] digits) {
        int[] d = new int[10];
        for (int num : digits) {
            d[num]++;
        }
        int res = 0;
        for (int num = 100; num < 999; num += 2) {
            if (valid(num, d)) {
                res++;
            }
        }
        return res;
    }

    public boolean valid(int num, int[] d) {
        int[] tmp = new int[10];
        while (num > 0) {
            tmp[num % 10]++;
            num /= 10;
        }
        for (int i = 0; i < 10; i++) {
            if (d[i] < tmp[i]) {
                return false;
            }
        }
        return true;
    }

    public int totalNumbers_v0(int[] digits) {
        int[] d = new int[10];
        for (int num : digits) {
            d[num]++;
        }
        int res = 0;
        // 枚举百位
        for (int i = 1; i < d.length; i++) {
            if (d[i] == 0) {
                continue;
            }
            d[i]--;
            res += dfs(0, i, d);
            d[i]++;
        }

        return res;
    }

    int dfs(int index, int num, int[] d) {
        if (index == 2) {
            // 第三个数如果是偶数返回 1
            return num % 2 == 0 ? 1 : 0;
        }
        int res = 0;
        // 枚举十位
        for (int i = 0; i < d.length; i++) {
            if (d[i] == 0) {
                continue;
            }
            d[i]--;
            res += dfs(index + 1, i, d);
            d[i]++;
        }
        return res;
    }

}

性能

2265.统计值等于子树平均值的节点数

目标

给你一棵二叉树的根节点 root ,找出并返回满足要求的节点数,要求节点的值等于其 子树 中值的 平均值 。

注意:

  • n 个元素的平均值可以由 n 个元素 求和 然后再除以 n ,并 向下舍入 到最近的整数。
  • root 的 子树 由 root 和它的所有后代组成。

示例 1:

输入:root = [4,8,5,0,1,null,6]
输出:5
解释:
对值为 4 的节点:子树的平均值 (4 + 8 + 5 + 0 + 1 + 6) / 6 = 24 / 6 = 4 。
对值为 5 的节点:子树的平均值 (5 + 6) / 2 = 11 / 2 = 5 。
对值为 0 的节点:子树的平均值 0 / 1 = 0 。
对值为 1 的节点:子树的平均值 1 / 1 = 1 。
对值为 6 的节点:子树的平均值 6 / 1 = 6 。

示例 2:

输入:root = [1]
输出:1
解释:对值为 1 的节点:子树的平均值 1 / 1 = 1。

说明:

  • 树中节点数目在范围 [1, 1000] 内
  • 0 <= Node.val <= 1000

思路

有一颗二叉树,统计其中节点值等于子树平均值(节点本身及其子树的节点值之和除以节点个数向下取整)的节点个数。

dfs 依题意统计即可。

代码


/**
 * @date 2026-09-10 8:56
 */
public class AverageOfSubtree2265 {

    int res = 0;

    public int averageOfSubtree(TreeNode root) {
        dfs(root);
        return res;
    }

    public class Dto {
        public int sum;
        public int num;

        public Dto() {
        }

        public Dto(int sum, int num) {
            this.sum = sum;
            this.num = num;
        }
    }

    public Dto dfs(TreeNode node) {
        if (node == null) {
            return new Dto();
        }
        Dto cur = new Dto(node.val, 1);
        Dto l = dfs(node.left);
        Dto r = dfs(node.right);
        cur.sum += l.sum + r.sum;
        cur.num += l.num + r.num;
        if (node.val == cur.sum / cur.num) {
            res++;
        }
        return cur;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */

性能

3871.统计范围内的逗号II

目标

给你一个整数 n。

返回将所有从 [1, n](包含两端)范围内的整数以 标准 数字格式书写时所用到的 逗号总数。

在 标准 格式中:

  • 从右边开始,每 三位 数字后插入一个逗号。
  • 位数 少于四位 的数字不包含逗号。

示例 1:

输入: n = 1002
输出: 3
解释:
数字 "1,000"、"1,001" 和 "1,002" 每个都包含一个逗号,总计 3 个逗号。

示例 2:

输入: n = 998
输出: 0
解释:
从 1 到 998 的所有数字位数都少于四位,因此没有使用逗号。

说明:

  • 1 <= n <= 10^15

思路

返回 [1, n] 之间所有整数的标准写法中总共有多少逗号。所谓标准写法指从右开始每 3 个数字插入一个逗号,且逗号不能位于开头。

  • 1,000 ~ 999,999 之间的数字有 1 个逗号
  • 1,000,000 ~ 999,999,999 之间的数字有 2 个逗号
  • 1,000,000,000 ~ 999,999,999,999 之间的数字有 3 个逗号
  • 1,000,000,000,000 ~ 999,999,999,999,999 之间的数字有 4 个逗号
  • 1,000,000,000,000,0005 个逗号

返回 max(0, n - 999) + max(0, n - 999999) + max(0, n - 999999999) + max(0, n - 999999999999) + max(0, n - 999999999999999)

可以写成循环的形式,for (long i = 1000; i <= n; i *= 1000) { res += n - i + 1; }

代码


/**
 * @date 2026-09-08 9:15
 */
public class CountCommas3871 {

    public long countCommas(long n) {
        long res = 0;
        for (long i = 1000; i <= n; i *= 1000) {
            res += n - i + 1;
        }
        return res;
    }
}

性能

3870.统计范围内的逗号

目标

给你一个整数 n。

返回将所有从 [1, n](包含两端)范围内的整数以 标准 数字格式书写时所用到的 逗号总数。

在 标准 格式中:

  • 从右边开始,每 三位 数字后插入一个逗号。
  • 位数 少于四位 的数字不包含逗号。

示例 1:

输入: n = 1002
输出: 3
解释:
数字 "1,000"、"1,001" 和 "1,002" 每个都包含一个逗号,总计 3 个逗号。

示例 2:

输入: n = 998
输出: 0
解释:
从 1 到 998 的所有数字位数都少于四位,因此没有使用逗号。

说明:

  • 1 <= n <= 10^5

思路

返回 [1, n] 之间所有整数的标准写法中总共有多少逗号。所谓标准写法指从右开始每 3 个数字插入一个逗号,且逗号不能位于开头。

只需判断 1 ~ n 之间有多少个数字在 1,000 ~ 100,000 之间,返回 max(0, n - 999) 即可。

代码


/**
 * @date 2026-09-08 9:04
 */
public class CountCommas3870 {

    public int countCommas(int n) {
        return Math.max(0, n - 999);
    }
}

性能

3904.最小稳定下标II

目标

给你一个长度为 n 的整数数组 nums 和一个整数 k。

对于每个下标 i,定义它的 不稳定值 为 max(nums[0..i]) - min(nums[i..n - 1])。

换句话说:

  • max(nums[0..i]) 表示从下标 0 到下标 i 的元素中的 最大值 。
  • min(nums[i..n - 1]) 表示从下标 i 到下标 n - 1 的元素中的 最小值 。

如果某个下标 i 的不稳定值 小于等于 k,则称该下标为 稳定下标 。

返回 最小 的稳定下标。如果不存在这样的下标,则返回 -1。

示例 1:

输入: nums = [5,0,1,4], k = 3
输出: 3
解释:
在下标 0 处:[5] 中的最大值是 5,[5, 0, 1, 4] 中的最小值是 0,因此不稳定值为 5 - 0 = 5。
在下标 1 处:[5, 0] 中的最大值是 5,[0, 1, 4] 中的最小值是 0,因此不稳定值为 5 - 0 = 5。
在下标 2 处:[5, 0, 1] 中的最大值是 5,[1, 4] 中的最小值是 1,因此不稳定值为 5 - 1 = 4。
在下标 3 处:[5, 0, 1, 4] 中的最大值是 5,[4] 中的最小值是 4,因此不稳定值为 5 - 4 = 1。
这是第一个不稳定值小于等于 k = 3 的下标,因此答案是 3。

示例 2:

输入: nums = [3,2,1], k = 1
输出: -1
解释:
在下标 0 处,不稳定值为 3 - 1 = 2。
在下标 1 处,不稳定值为 3 - 1 = 2。
在下标 2 处,不稳定值为 3 - 1 = 2。
这些值都不小于等于 k = 1,因此答案是 -1。

示例 3:

输入: nums = [0], k = 0
输出: 0
解释:
在下标 0 处,不稳定值为 0 - 0 = 0,它小于等于 k = 0。因此答案是 0。

说明:

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

思路

3903.最小稳定下标I 相比数据范围扩大了。

代码


/**
 * @date 2026-09-07 9:01
 */
public class FirstStableIndex3904 {

    public int firstStableIndex(int[] nums, int k) {
        int n = nums.length;
        int[] suffix = new int[n + 1];
        Arrays.fill(suffix, Integer.MAX_VALUE);
        for (int i = n - 1; i >= 0; i--) {
            suffix[i] = Math.min(suffix[i + 1], nums[i]);
        }
        int max = 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(max, nums[i]);
            if (max - suffix[i] <= k) {
                return i;
            }
        }
        return -1;
    }
}

性能