Kimi LeetCode 3533. 判断连接可整除性 Rust实现
根据搜索结果,我已经了解了 LeetCode 3533 的题目要求和解题思路。这是一个状态压缩动态规划问题,核心思路是:
1. 排序:先对 `nums` 排序,这样按顺序搜索时,第一个找到的可行解就是字典序最小的
2. 预处理:计算每个数字的位数和 `10^len % k`
3. 状态压缩 DP:`dp[mask][mod]` 表示已选数字集合为 `mask`,当前拼接数模 `k` 为 `mod` 是否可行
4. 路径重建:通过记忆化搜索重建字典序最小的排列
以下是 Rust 实现:
```rust
use std::collections::HashMap;
impl Solution {
pub fn concatenated_divisibility(nums: Vec<i32>, k: i32) -> Vec<i32> {
let n = nums.len();
let k = k as usize;
let mut nums = nums;
// 排序,保证按顺序找第一个可行解就是字典序最小的
nums.sort();
// 预处理:计算每个数字的位数和 10^len % k
let mut lengths = vec![0; n];
let mut pow10_mod = vec![0; n];
for i in 0..n {
let num = nums[i] as usize;
let len = num.to_string().len();
lengths[i] = len;
// 计算 10^len % k
let mut pow = 1usize;
for _ in 0..len {
pow = (pow * 10) % k;
}
pow10_mod[i] = pow;
}
// 状态压缩 DP
// dp[mask][mod] = true/false,表示是否可行
// 使用 HashMap 或二维数组,这里用 Vec<Vec<Option<bool>>>
let mut memo = vec![vec![None; k]; 1 << n];
// 先判断是否存在可行解
if !Self::dfs(&nums, &pow10_mod, &mut memo, k, 0, 0, n) {
return vec![];
}
// 重建路径:按排序后的顺序,每次选第一个能导致可行解的数字
let mut result = Vec::with_capacity(n);
let mut mask = 0usize;
let mut mod_val = 0usize;
for _ in 0..n {
for i in 0..n {
if (mask >> i) & 1 == 1 {
continue; // 已选过
}
let new_mod = (mod_val * pow10_mod[i] + nums[i] as usize) % k;
let new_mask = mask | (1 << i);
// 检查选这个数后是否仍能完成
if Self::dfs(&nums, &pow10_mod, &mut memo, k, new_mask, new_mod, n) {
result.push(nums[i]);
mask = new_mask;
mod_val = new_mod;
break;
}
}
}
result
}
fn dfs(
nums: &[i32],
pow10_mod: &[usize],
memo: &mut Vec<Vec<Option<bool>>>,
k: usize,
mask: usize,
mod_val: usize,
n: usize,
) -> bool {
// 所有数字都已选完
if mask == (1 << n) - 1 {
return mod_val == 0;
}
// 记忆化
if let Some(res) = memo[mask][mod_val] {
return res;
}
// 尝试每个未选的数字
for i in 0..n {
if (mask >> i) & 1 == 1 {
continue;
}
let new_mod = (mod_val * pow10_mod[i] + nums[i] as usize) % k;
let new_mask = mask | (1 << i);
if Self::dfs(nums, pow10_mod, memo, k, new_mask, new_mod, n) {
memo[mask][mod_val] = Some(true);
return true;
}
}
memo[mask][mod_val] = Some(false);
false
}
}
```
关键思路说明:
1. 排序保证字典序:先对 `nums` 排序,在 DFS 和重建路径时都按升序尝试,这样第一个找到的可行解就是字典序最小的排列。
2. 模运算拼接:拼接 `a` 和 `b` 的数学表示为 `a * 10^len(b) + b`。在模 `k` 意义下,如果当前余数为 `mod`,拼接数字 `nums[i]`(长度为 `len`)后的新余数为:
```
new_mod = (mod * (10^len % k) + nums[i]) % k
```
3. 状态压缩:用 `mask`(二进制位)表示已选数字集合,`dp[mask][mod]` 记录该状态是否可行,避免重复计算。
4. 路径重建:先通过 DFS 填充记忆化表,然后贪心地在每一步选择排序后第一个能导致可行解的数字,保证字典序最小。
复杂度:
- 时间:O(n × 2^n × k),其中 n ≤ 13,k ≤ 100
- 空间:O(2^n × k)

更多推荐



所有评论(0)