Kimi LeetCode 3343. 统计平衡排列的数目 Rust实现

以下是 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 是偶数)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值