以下是 LeetCode 3343. 统计平衡排列的数目的 Rust 实现。这道题的核心思路是记忆化搜索 + 组合数学。
思路分析
- 平衡排列定义:偶数位(0,2,4...)数字之和等于奇数位(1,3,5...)数字之和
- 设字符串长度为 n,偶数位有 `(n+1)/2` 个,奇数位有 `n/2` 个
- 先统计每个数字出现次数,计算总和。如果总和为奇数,直接返回 0
- 目标:从每个数字的出现次数中,分配一部分到偶数位,一部分到奇数位,使得偶数位数字之和等于总和的一半
Rust 代码
```rust
const MOD: i64 = 1_000_000_007;
const MX: usize = 80;
// 预处理组合数 C[n][k]
static mut C: [[i64; MX]; MX] = [[0; MX]; MX];
fn init_comb() {
unsafe {
C[0][0] = 1;
for i in 1..MX {
C[i][0] = 1;
for j in 1..=i {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
}
}
}
impl Solution {
pub fn count_balanced_permutations(num: String) -> i32 {
init_comb();
let mut cnt = [0; 10];
let mut s = 0;
for ch in num.chars() {
let d = (ch as u8 - b'0') as usize;
cnt[d] += 1;
s += d;
}
// 如果总和为奇数,无法平衡
if s % 2 == 1 {
return 0;
}
let n = num.len();
let target = s / 2;
let even_slots = n / 2; // 偶数位个数
let odd_slots = (n + 1) / 2; // 奇数位个数
// 记忆化数组: f[i][j][a][b]
// i: 当前处理的数字(0-9)
// j: 偶数位还需要凑的和
// a: 偶数位还剩下的空位
// b: 奇数位还剩下的空位
let mut memo = vec![vec![vec![vec![-1i64; odd_slots + 1]; even_slots + 1]; target + 1]; 10];
fn dfs(
i: usize,
j: usize,
a: usize,
b: usize,
cnt: &[usize; 10],
memo: &mut Vec<Vec<Vec<Vec<i64>>>>,
) -> i64 {
if i > 9 {
return if (j | a | b) == 0 { 1 } else { 0 };
}
if a == 0 && j > 0 {
return 0;
}
if memo[i][j][a][b] != -1 {
return memo[i][j][a][b];
}
let mut ans = 0i64;
let max_l = cnt[i].min(a);
for l in 0..=max_l {
let r = cnt[i] - l; // 放到奇数位的个数
if r <= b && l * i <= j {
unsafe {
let ways = C[a][l] * C[b][r] % MOD;
let next = dfs(i + 1, j - l * i, a - l, b - r, cnt, memo);
ans = (ans + ways * next % MOD) % MOD;
}
}
}
memo[i][j][a][b] = ans;
ans
}
dfs(0, target, even_slots, odd_slots, &cnt, &mut memo) as i32
}
}
```
核心思路说明
要点 说明
状态定义 `dfs(i, j, a, b)` 表示处理到数字 `i` 时,偶数位还需凑 `j` 的和,还剩 `a` 个偶数位空位和 `b` 个奇数位空位
转移 枚举数字 `i` 放到偶数位的个数 `l`,奇数位自动放 `cnt[i]-l` 个
组合数 用 `C(a, l) * C(b, r)` 计算当前数字的放置方案数(Rust 中需要 `unsafe` 访问静态数组)
边界 处理完 0-9 所有数字后,检查 `j==0 && a==0 && b==0`
复杂度
- 时间复杂度: O(10 \times n^2 \times (n + 10)) — 其中 10 是数字种类数
- 空间复杂度: O(10 \times n^2 \times (n/2+1)) — 记忆化数组大小
参考来源
- (Doocs LeetCode 官方题解,提供了 C++、Go、TypeScript 参考实现)
- (Mocowcow 题解,提供了 Python 参考实现)
- (CSDN 题解,提供了 Java 参考实现)
注意事项
1. Rust 中访问 `static mut` 需要 `unsafe` 块,这是 Rust 的安全特性
2. 组合数预处理只需一次,可以用 `Once` 或 `LazyLock` 优化(Rust 1.80+)
3. 记忆化数组用 `-1` 作为未计算标记,因为结果非负
4. 注意 `even_slots` 和 `odd_slots` 的定义:偶数位个数是 `n/2`,奇数位个数是 `(n+1)/2`(因为索引从 0 开始,0 是偶数)
15

被折叠的 条评论
为什么被折叠?



