3445.奇偶频次间的最大差值II

目标

给你一个字符串 s 和一个整数 k 。请你找出 s 的子字符串 subs 中两个字符的出现频次之间的 最大 差值,freq[a] - freq[b] ,其中:

  • subs 的长度 至少 为 k 。
  • 字符 a 在 subs 中出现奇数次。
  • 字符 b 在 subs 中出现偶数次。

返回 最大 差值。

注意 ,subs 可以包含超过 2 个 互不相同 的字符。.

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

示例 1:

输入:s = "12233", k = 4
输出:-1
解释:
对于子字符串 "12233" ,'1' 的出现次数是 1 ,'3' 的出现次数是 2 。差值是 1 - 2 = -1 。

示例 2:

输入:s = "1122211", k = 3
输出:1
解释:
对于子字符串 "11222" ,'2' 的出现次数是 3 ,'1' 的出现次数是 2 。差值是 3 - 2 = 1 。

示例 3:

输入:s = "110", k = 3
输出:-1

说明:

  • 3 <= s.length <= 3 * 10^4
  • s 仅由数字 '0' 到 '4' 组成。
  • 输入保证至少存在一个子字符串是由一个出现奇数次的字符和一个出现偶数次的字符组成。
  • 1 <= k <= s.length

思路

有一个字符串 s 仅由 0 ~ 4 组成,求其长度至少为 k 的子串中,3442_奇偶频次间的最大差值I 的最大值。

// todo

代码

性能

440.字典序的第K小数字

目标

给定整数 n 和 k,返回 [1, n] 中字典序第 k 小的数字。

示例 1:

输入: n = 13, k = 2
输出: 10
解释: 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。

示例 2:

输入: n = 1, k = 1
输出: 1

说明:

  • 1 <= k <= n <= 10^9

思路

1 ~ n 的所有数字按字典序排列后的第 k 个数字。

本来想要参考 386.字典序排数 取第 k 个数字。但是数据范围太大,O(n) 的解法不可行。

题目标签显示的是字典树。想象一棵 10 叉树,根为 0,从第一个孩子 1 开始,如果其子树大小小于剩余的数字个数,直接跳过,访问兄弟节点 2,否则进入下一层,从第一个孩子 10 开始,计算其子树大小,如果小于剩余数字个数,访问兄弟节点 11,否则进入下一层。以此类推。

如何统计子树的大小?可以将当前根节点 l 与兄弟节点 r 一起向下计算,当前层的节点个数 = Math.min(r, n + 1) - 1 - l + 1Math.min(r, n + 1) - l。比如,l == 100, r == 200,计算 100 ~ 200 - 1 的数字个数。注意 rn + 1 取最小值,因为要减 1 所以比较的是 n + 1

代码


/**
 * @date 2025-06-09 8:44
 */
public class FindKthNumber440 {

    public int findKthNumber(int n, int k) {
        int i = 1;
        k--;
        while (k > 0) {
            int cnt = subTreeNodesCnt(i, n);
            if (cnt <= k) {
                k -= cnt;
                i++;
            } else {
                k--;
                i *= 10;
            }
        }

        return i;
    }

    public int subTreeNodesCnt(int root, int n) {
        int cnt = 0;
        long l = root;
        long r = root + 1;
        while (l <= n) {
            cnt += Math.min(r, n + 1) - l;
            l *= 10;
            r *= 10;
        }
        return cnt;
    }
}

性能

1298.你能从盒子里获得的最大糖果数

目标

给你 n 个盒子,每个盒子的格式为 [status, candies, keys, containedBoxes] ,其中:

  • 状态字 status[i]:整数,如果 box[i] 是开的,那么是 1 ,否则是 0 。
  • 糖果数 candies[i]: 整数,表示 box[i] 中糖果的数目。
  • 钥匙 keys[i]:数组,表示你打开 box[i] 后,可以得到一些盒子的钥匙,每个元素分别为该钥匙对应盒子的下标。
  • 内含的盒子 containedBoxes[i]:整数,表示放在 box[i] 里的盒子所对应的下标。

给你一个 initialBoxes 数组,表示你现在得到的盒子,你可以获得里面的糖果,也可以用盒子里的钥匙打开新的盒子,还可以继续探索从这个盒子里找到的其他盒子。

请你按照上述规则,返回可以获得糖果的 最大数目 。

示例 1:

输入:status = [1,0,1,0], candies = [7,5,4,100], keys = [[],[],[1],[]], containedBoxes = [[1,2],[3],[],[]], initialBoxes = [0]
输出:16
解释:
一开始你有盒子 0 。你将获得它里面的 7 个糖果和盒子 1 和 2。
盒子 1 目前状态是关闭的,而且你还没有对应它的钥匙。所以你将会打开盒子 2 ,并得到里面的 4 个糖果和盒子 1 的钥匙。
在盒子 1 中,你会获得 5 个糖果和盒子 3 ,但是你没法获得盒子 3 的钥匙所以盒子 3 会保持关闭状态。
你总共可以获得的糖果数目 = 7 + 4 + 5 = 16 个。

示例 2:

输入:status = [1,0,0,0,0,0], candies = [1,1,1,1,1,1], keys = [[1,2,3,4,5],[],[],[],[],[]], containedBoxes = [[1,2,3,4,5],[],[],[],[],[]], initialBoxes = [0]
输出:6
解释:
你一开始拥有盒子 0 。打开它你可以找到盒子 1,2,3,4,5 和它们对应的钥匙。
打开这些盒子,你将获得所有盒子的糖果,所以总糖果数为 6 个。

示例 3:

输入:status = [1,1,1], candies = [100,1,100], keys = [[],[0,2],[]], containedBoxes = [[],[],[]], initialBoxes = [1]
输出:1

示例 4:

输入:status = [1], candies = [100], keys = [[]], containedBoxes = [[]], initialBoxes = []
输出:0

示例 5:

输入:status = [1,1,1], candies = [2,3,2], keys = [[],[],[]], containedBoxes = [[],[],[]], initialBoxes = [2,1,0]
输出:7

说明:

  • 1 <= status.length <= 1000
  • status.length == candies.length == keys.length == containedBoxes.length == n
  • status[i] 要么是 0 要么是 1 。
  • 1 <= candies[i] <= 1000
  • 0 <= keys[i].length <= status.length
  • 0 <= keys[i][j] < status.length
  • keys[i] 中的值都是互不相同的。
  • 0 <= containedBoxes[i].length <= status.length
  • 0 <= containedBoxes[i][j] < status.length
  • containedBoxes[i] 中的值都是互不相同的。
  • 每个盒子最多被一个盒子包含。
  • 0 <= initialBoxes.length <= status.length
  • 0 <= initialBoxes[i] < status.length

思路

开始时有一些盒子 initialBoxes 元素值表示盒子下标,盒子的状态用 status 数组表示,0 表示盒子被锁住,1 表示盒子是打开的。盒子中装有糖果、也可能装有其它盒子,或者盒子的钥匙。求能够获得的最大糖果数。

bfs 使用优先队列对盒子状态排序,优先取开着的盒子。将开着的盒子中的盒子放入队列,并且用盒子中的钥匙修改队列中的盒子状态。记录已经开过的盒子,避免重复计数。

代码


/**
 * @date 2025-06-03 20:30
 */
public class MaxCandies1298 {

    public int maxCandies(int[] status, int[] candies, int[][] keys, int[][] containedBoxes, int[] initialBoxes) {
        int n = status.length;
        int[][] boxes = new int[n][2];
        for (int i = 0; i < n; i++) {
            boxes[i] = new int[]{i, status[i]};
        }
        PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> b[1] - a[1]);
        for (int initialBox : initialBoxes) {
            q.offer(boxes[initialBox]);
        }
        int res = 0;
        Set<Integer> visited = new HashSet<>();
        while (!q.isEmpty() && q.peek()[1] == 1) {
            while (!q.isEmpty() && q.peek()[1] == 1) {
                int i = q.poll()[0];
                visited.add(i);
                res += candies[i];
                for (int j : keys[i]) {
                    boxes[j][1] = 1;
                }
                for (int j : containedBoxes[i]) {
                    if (visited.contains(j)) {
                        continue;
                    }
                    q.offer(boxes[j]);
                }
            }
        }
        return res;
    }
}

性能

135.分发糖果

目标

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

示例 1:

输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

说明:

  • n == ratings.length
  • 1 <= n <= 2 * 10^4
  • 0 <= ratings[i] <= 2 * 10^4

思路

n 个孩子站成一排,ratings 表示每个孩子的评分。现在需要给孩子分发糖果,要求每个孩子至少分配一个糖果,并且相邻的两个孩子,评分高的孩子分得的糖果大于评分低的孩子分得的糖果,返回需要准备的最少糖果数目。

官网题解提供了一种常数空间复杂度的写法,记录评分严格递增与严格递减序列长度:

  • 当处于严格递增序列时,分配的糖果比前面的多一个即可
  • 如果评分相同,则分配一个糖果并重置序列长度
  • 当处于严格递减序列时,分配递减序列长度个糖果,相当于递增序列的逆过程
  • 需要特殊处理递增序列与递减序列长度相同的情况

看下面的例子:

评分 4 5 4 3 2 1,
    a b c d e f
    1 2 1
当处理到 c 时,处于下降序列,我们给他分配 1 个糖果
    1 3 2 1
当处理到 d 时,仍处于下降序列,需要给 c 多分配一个糖果,即 2 个,发现这时与 b 分配的相同,不满足要求,需要给 b 也多分配一个,共 3 个
    1 4 3 2 1
当处理到 e 时,仍处于下降序列,需要给 b c d 多分配一个,共 4 个
    1 5 4 3 2 1
当处理到 f 时,仍处于下降序列,需要给 b c d e 多分配一个糖果,共 5 个

相当于往递减序列的头部插入递减序列长度个糖果,视为反向的递增,当递增序列与递减序列长度相同时需要特殊处理。

代码


/**
 * @date 2024-12-11 9:18
 */
public class Candy135 {

    public int candy_v3(int[] ratings) {
        int n = ratings.length;
        int increase = 1;
        int decrease = 0;
        int res = 1;
        int prev = 1;
        for (int i = 1; i < n; i++) {
            if (ratings[i] >= ratings[i - 1]) {
                prev = ratings[i] == ratings[i - 1] ? 1 : prev + 1;
                res += prev;
                increase = prev;
                decrease = 0;
            } else {
                decrease++;
                if (decrease == increase) {
                    decrease++;
                }
                res += decrease;
                prev = 1;
            }
        }
        return res;
    }

}

性能

3373.连接两棵树后最大目标节点数目II

目标

有两棵 无向 树,分别有 n 和 m 个树节点。两棵树中的节点编号分别为[0, n - 1] 和 [0, m - 1] 中的整数。

给你两个二维整数 edges1 和 edges2 ,长度分别为 n - 1 和 m - 1 ,其中 edges1[i] = [ai, bi] 表示第一棵树中节点 ai 和 bi 之间有一条边,edges2[i] = [ui, vi] 表示第二棵树中节点 ui 和 vi 之间有一条边。

如果节点 u 和节点 v 之间路径的边数是偶数,那么我们称节点 u 是节点 v 的 目标节点 。注意 ,一个节点一定是它自己的 目标节点 。

请你返回一个长度为 n 的整数数组 answer ,answer[i] 表示将第一棵树中的一个节点与第二棵树中的一个节点连接一条边后,第一棵树中节点 i 的 目标节点 数目的 最大值 。

注意 ,每个查询相互独立。意味着进行下一次查询之前,你需要先把刚添加的边给删掉。

示例 1:

输入:edges1 = [[0,1],[0,2],[2,3],[2,4]], edges2 = [[0,1],[0,2],[0,3],[2,7],[1,4],[4,5],[4,6]]
输出:[8,7,7,8,8]
解释:
对于 i = 0 ,连接第一棵树中的节点 0 和第二棵树中的节点 0 。
对于 i = 1 ,连接第一棵树中的节点 1 和第二棵树中的节点 4 。
对于 i = 2 ,连接第一棵树中的节点 2 和第二棵树中的节点 7 。
对于 i = 3 ,连接第一棵树中的节点 3 和第二棵树中的节点 0 。
对于 i = 4 ,连接第一棵树中的节点 4 和第二棵树中的节点 4 。

示例 2:

输入:edges1 = [[0,1],[0,2],[0,3],[0,4]], edges2 = [[0,1],[1,2],[2,3]]
输出:[3,6,6,6,6]
解释:
对于每个 i ,连接第一棵树中的节点 i 和第二棵树中的任意一个节点。

提示:

  • 2 <= n, m <= 10^5
  • edges1.length == n - 1
  • edges2.length == m - 1
  • edges1[i].length == edges2[i].length == 2
  • edges1[i] = [ai, bi]
  • 0 <= ai, bi < n
  • edges2[i] = [ui, vi]
  • 0 <= ui, vi < m
  • 输入保证 edges1 和 edges2 都表示合法的树。

思路

有两棵树,当节点 ab 之间的边数为偶数时,称 ab 的目标节点,在两棵树任意节点之间连一条边,针对第一棵树的所有节点,计算其目标节点的最大个数。

3372.连接两棵树后最大目标节点数目I 相比,目标节点的条件从边数小于等于 k 变为了 偶数。

同一颗树中,某一个节点经过 偶数 或者 奇数条边到达的节点个数是固定的。不妨固定一个节点为根节点,经过 偶数 条边到达的节点集合为 A,经过 奇数 条边到达的节点集合为 B。集合 A 中节点的目标节点还是 A 中的节点,同理 B 中节点的目标节点还是 B 中的节点。

因此问题转化为计算两棵树的集合 A B,判断第一颗树的节点所属集合,如果属于 A,则取 A1 + Math.max(A2, B2),否则取 B1 + Math.max(A2, B2)。

核心点是固定一个参考节点,根据到达该节点的边数将节点分类,可以快速计算出目标节点个数,如果针对每一节点暴力搜索目标节点个数肯定会超时。

代码


/**
 * @date 2025-05-29 23:50
 */
public class MaxTargetNodes3373 {

    public int[] maxTargetNodes(int[][] edges1, int[][] edges2) {
        int n = edges1.length + 1;
        int m = edges2.length + 1;
        List<Integer>[] g1 = new List[n];
        List<Integer>[] g2 = new List[m];
        Arrays.setAll(g1, x -> new ArrayList<>());
        Arrays.setAll(g2, x -> new ArrayList<>());
        for (int[] edge : edges1) {
            int a = edge[0];
            int b = edge[1];
            g1[a].add(b);
            g1[b].add(a);
        }
        for (int[] edge : edges2) {
            int a = edge[0];
            int b = edge[1];
            g2[a].add(b);
            g2[b].add(a);
        }
        Set<Integer>[] set1 = new HashSet[2];
        Arrays.setAll(set1, x -> new HashSet<>());
        int[] set2 = new int[]{0, 0};
        dfs(0, -1, g1, 0, set1);
        dfs(0, -1, g2, 0, set2);
        int max2 = Math.max(set2[0], set2[1]);
        int[] res = new int[n];
        for (int i = 0; i < n; i++) {
            if (set1[0].contains(i)) {
                res[i] = set1[0].size() + max2;
            } else {
                res[i] = set1[1].size() + max2;
            }
        }
        return res;
    }

    public void dfs(int cur, int parent, List<Integer>[] g, int flag, Set<Integer>[] set) {
        set[flag].add(cur);
        for (Integer next : g[cur]) {
            if (next == parent) {
                continue;
            }
            dfs(next, cur, g, flag ^ 1, set);
        }
    }

    public void dfs(int cur, int parent, List<Integer>[] g, int flag, int[] res) {
        res[flag]++;
        for (Integer next : g[cur]) {
            if (next == parent) {
                continue;
            }
            dfs(next, cur, g, flag ^ 1, res);
        }
    }
}

性能

1857.有向图中最大颜色值

目标

给你一个 有向图 ,它含有 n 个节点和 m 条边。节点编号从 0 到 n - 1 。

给你一个字符串 colors ,其中 colors[i] 是小写英文字母,表示图中第 i 个节点的 颜色 (下标从 0 开始)。同时给你一个二维数组 edges ,其中 edges[j] = [aj, bj] 表示从节点 aj 到节点 bj 有一条 有向边 。

图中一条有效 路径 是一个点序列 x1 -> x2 -> x3 -> ... -> xk ,对于所有 1 <= i < k ,从 xi 到 xi+1 在图中有一条有向边。路径的 颜色值 是路径中 出现次数最多 颜色的节点数目。

请你返回给定图中有效路径里面的 最大颜色值 。如果图中含有环,请返回 -1 。

示例 1:

输入:colors = "abaca", edges = [[0,1],[0,2],[2,3],[3,4]]
输出:3
解释:路径 0 -> 2 -> 3 -> 4 含有 3 个颜色为 "a" 的节点(上图中的红色节点)。

示例 2:

输入:colors = "a", edges = [[0,0]]
输出:-1
解释:从 0 到 0 有一个环。

说明:

  • n == colors.length
  • m == edges.length
  • 1 <= n <= 10^5
  • 0 <= m <= 10^5
  • colors 只含有小写英文字母。
  • 0 <= aj, bj < n

思路

// 今天没空做了 todo

代码

性能

3068.最大节点价值之和

目标

给你一棵 n 个节点的 无向 树,节点从 0 到 n - 1 编号。树以长度为 n - 1 下标从 0 开始的二维整数数组 edges 的形式给你,其中 edges[i] = [ui, vi] 表示树中节点 ui 和 vi 之间有一条边。同时给你一个 正 整数 k 和一个长度为 n 下标从 0 开始的 非负 整数数组 nums ,其中 nums[i] 表示节点 i 的 价值 。

Alice 想 最大化 树中所有节点价值之和。为了实现这一目标,Alice 可以执行以下操作 任意 次(包括 0 次):

  • 选择连接节点 u 和 v 的边 [u, v] ,并将它们的值更新为:
    • nums[u] = nums[u] XOR k
    • nums[v] = nums[v] XOR k

请你返回 Alice 通过执行以上操作 任意次 后,可以得到所有节点 价值之和 的 最大值 。

示例 1:

输入:nums = [1,2,1], k = 3, edges = [[0,1],[0,2]]
输出:6
解释:Alice 可以通过一次操作得到最大价值和 6 :
- 选择边 [0,2] 。nums[0] 和 nums[2] 都变为:1 XOR 3 = 2 ,数组 nums 变为:[1,2,1] -> [2,2,2] 。
所有节点价值之和为 2 + 2 + 2 = 6 。
6 是可以得到最大的价值之和。

示例 2:

输入:nums = [2,3], k = 7, edges = [[0,1]]
输出:9
解释:Alice 可以通过一次操作得到最大和 9 :
- 选择边 [0,1] 。nums[0] 变为:2 XOR 7 = 5 ,nums[1] 变为:3 XOR 7 = 4 ,数组 nums 变为:[2,3] -> [5,4] 。
所有节点价值之和为 5 + 4 = 9 。
9 是可以得到最大的价值之和。

示例 3:

输入:nums = [7,7,7,7,7,7], k = 3, edges = [[0,1],[0,2],[0,3],[0,4],[0,5]]
输出:42
解释:Alice 不需要执行任何操作,就可以得到最大价值之和 42 。

说明:

  • 2 <= n == nums.length <= 2 * 10^4
  • 1 <= k <= 10^9
  • 0 <= nums[i] <= 10^9
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= edges[i][0], edges[i][1] <= n - 1
  • 输入保证 edges 构成一棵合法的树。

思路

// todo

代码

性能

1931.用三种不同颜色为网格涂色

目标

给你两个整数 m 和 n 。构造一个 m x n 的网格,其中每个单元格最开始是白色。请你用 红、绿、蓝 三种颜色为每个单元格涂色。所有单元格都需要被涂色。

涂色方案需要满足:不存在相邻两个单元格颜色相同的情况 。返回网格涂色的方法数。因为答案可能非常大, 返回 对 10^9 + 7 取余 的结果。

示例 1:

输入:m = 1, n = 1
输出:3
解释:如上图所示,存在三种可能的涂色方案。

示例 2:

输入:m = 1, n = 2
输出:6
解释:如上图所示,存在六种可能的涂色方案。

示例 3:

输入:m = 5, n = 5
输出:580986

说明:

  • 1 <= m <= 5
  • 1 <= n <= 1000

思路

// todo 状压DP

代码

性能

3337.字符串转换后的长度II

目标

给你一个由小写英文字母组成的字符串 s,一个整数 t 表示要执行的 转换 次数,以及一个长度为 26 的数组 nums。每次 转换 需要根据以下规则替换字符串 s 中的每个字符:

  • 将 s[i] 替换为字母表中后续的 nums[s[i] - 'a'] 个连续字符。例如,如果 s[i] = 'a' 且 nums[0] = 3,则字符 'a' 转换为它后面的 3 个连续字符,结果为 "bcd"。
  • 如果转换超过了 'z',则 回绕 到字母表的开头。例如,如果 s[i] = 'y' 且 nums[24] = 3,则字符 'y' 转换为它后面的 3 个连续字符,结果为 "zab"。

返回 恰好 执行 t 次转换后得到的字符串的 长度。

由于答案可能非常大,返回其对 10^9 + 7 取余的结果。

示例 1:

输入: s = "abcyy", t = 2, nums = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2]
输出: 7
解释:
第一次转换 (t = 1)
'a' 变为 'b' 因为 nums[0] == 1
'b' 变为 'c' 因为 nums[1] == 1
'c' 变为 'd' 因为 nums[2] == 1
'y' 变为 'z' 因为 nums[24] == 1
'y' 变为 'z' 因为 nums[24] == 1
第一次转换后的字符串为: "bcdzz"
第二次转换 (t = 2)
'b' 变为 'c' 因为 nums[1] == 1
'c' 变为 'd' 因为 nums[2] == 1
'd' 变为 'e' 因为 nums[3] == 1
'z' 变为 'ab' 因为 nums[25] == 2
'z' 变为 'ab' 因为 nums[25] == 2
第二次转换后的字符串为: "cdeabab"
字符串最终长度: 字符串为 "cdeabab",长度为 7 个字符。

示例 2:

输入: s = "azbk", t = 1, nums = [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
输出: 8
解释:
第一次转换 (t = 1)
'a' 变为 'bc' 因为 nums[0] == 2
'z' 变为 'ab' 因为 nums[25] == 2
'b' 变为 'cd' 因为 nums[1] == 2
'k' 变为 'lm' 因为 nums[10] == 2
第一次转换后的字符串为: "bcabcdlm"
字符串最终长度: 字符串为 "bcabcdlm",长度为 8 个字符。

说明:

  • 1 <= s.length <= 10^5
  • s 仅由小写英文字母组成。
  • 1 <= t <= 10^9
  • nums.length == 26
  • 1 <= nums[i] <= 25

思路

对字符串执行 t 次转换,求字符串的最终长度。每次转换需要将字符 s[i] 转换为字母表中后面的 nums[s[i] - 'a'] 个连续字符,如果超过了 z 则回到字母表开头。

3335.字符串转换后的长度I 的区别是替换为后面的连续 nums[s[i] - 'a'] 个字符。

定义 dp[k][i] 表示第 k 次转换后,字符 (char)(i + 'a') 的出现次数。但是转换次数高达 10^9 会超出内存限制。

转换次数太大,必须想办法降低它的复杂度。想到了倍增与快速幂,可以使用矩阵描述每一次字符的转换,考虑矩阵快速幂。

定义矩阵 matrix 的第 i 行表示字母 (char)(i + 'a') 转换后的字符集合,使用行向量表示, 第 j 列为 1 表示转换后的字母为 (char)(j + 'a')。于是问题转换为 vector * matrix^t,其中 vector 表示字符串每个字符的出现次数。

代码


/**
 * @date 2025-05-14 8:56
 */
public class LengthAfterTransformations3337 {

    public int lengthAfterTransformations(String s, int t, List<Integer> nums) {
        int mod = 1000000007;
        int[] vector = new int[26];
        char[] chars = s.toCharArray();
        for (char c : chars) {
            vector[c - 'a']++;
        }
        int[][] matrix = new int[26][26];
        for (int i = 0; i < 26; i++) {
            for (int j = 1; j <= nums.get(i); j++) {
                int index = (i + j) % 26;
                matrix[i][index] = 1;
            }
        }
        int[] cnt = pow(vector, matrix, t, mod);
        int res = 0;
        for (int num : cnt) {
            res = (res + num) % mod;
        }
        return res;
    }

    public int[] pow(int[] vector, int[][] matrix, int exponent, int mod) {
        int[] res = vector;
        while (exponent > 0) {
            if ((exponent & 1) == 1) {
                res = multiply(res, matrix, mod);
            }
            matrix = square(matrix, mod);
            exponent >>= 1;
        }
        return res;
    }

    public int[][] square(int[][] matrix, int mod) {
        int n = matrix.length;
        int[][] res = new int[n][n];
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
                for (int i = 0; i < n; i++) {
                    res[row][col] = (int) ((res[row][col] + (long) matrix[row][i] * matrix[i][col]) % mod);
                }
            }
        }
        return res;
    }

    public int[] multiply(int[] vector, int[][] matrix, int mod) {
        int n = vector.length;
        int[] res = new int[n];
        for (int col = 0; col < n; col++) {
            for (int i = 0; i < n; i++) {
                res[col] = (int) ((res[col] + (long) vector[i] * matrix[i][col]) % mod);
            }
        }
        return res;
    }

}

性能

3343.统计平衡排列的数目

目标

给你一个字符串 num 。如果一个数字字符串的奇数位下标的数字之和与偶数位下标的数字之和相等,那么我们称这个数字字符串是 平衡的 。

请你返回 num 不同排列 中,平衡 字符串的数目。

由于答案可能很大,请你将答案对 10^9 + 7 取余 后返回。

一个字符串的 排列 指的是将字符串中的字符打乱顺序后连接得到的字符串。

示例 1:

输入:num = "123"
输出:2
解释:
num 的不同排列包括: "123" ,"132" ,"213" ,"231" ,"312" 和 "321" 。
它们之中,"132" 和 "231" 是平衡的。所以答案为 2 。

示例 2:

输入:num = "112"
输出:1
解释:
num 的不同排列包括:"112" ,"121" 和 "211" 。
只有 "121" 是平衡的。所以答案为 1 。

示例 3:

输入:num = "12345"
输出:0
解释:
num 的所有排列都是不平衡的。所以答案为 0 。

说明:

  • 2 <= num.length <= 80
  • num 中的字符只包含数字 '0' 到 '9' 。

思路

//todo

代码

性能