当前位置: 首页 > news >正文

Kimi LeetCode 3518. 最小回文排列 II Rust实现

LeetCode 3518. 最小回文排列 II 的核心思路是:回文串由前半部分唯一确定(奇数长度时中间字符固定),因此问题转化为求前半部分字符串的第 k 小不同排列。

具体做法:
1. 提取前半部分 `half` 和中间字符 `mid`(若有)。
2. 用组合数连乘计算多重集排列数(避免阶乘溢出):`C(n, v1) * C(n-v1, v2) * ...`。由于 `k ≤ 10^6`,当乘积超过 `1_000_001` 时直接截断。
3. 若总排列数 `< k`,返回空串。
4. 逐位贪心构造:从小到大枚举 `'a'~'z'`,尝试将当前字符填入该位,计算剩余位置的排列数 `ways`。若 `k ≤ ways`,则确定该位;否则 `k -= ways` 并尝试下一个字符。
5. 最后将构造好的左半部分翻转拼接。

时间复杂度 `O(|half| * 26 * log(k))`,空间复杂度 `O(1)`。

```rust
impl Solution {
const MAX_K: i64 = 1_000_001;

pub fn smallest_palindrome(s: String, k: i32) -> String {
let mut k = k as i64;
let n = s.len();
let half = &s[..n / 2];
let mid = if n % 2 == 0 { "" } else { &s[n / 2..n / 2 + 1] };

let mut cnt = [0i32; 26];
for c in half.chars() {
cnt[(c as u8 - b'a') as usize] += 1;
}

// 总排列数不足 k
if Self::count_arrangements(&cnt) < k {
return String::new();
}

let half_len = half.len();
let mut ans = String::with_capacity(half_len);

for _ in 0..half_len {
for i in 0..26 {
if cnt[i] == 0 {
continue;
}
// 尝试填入字符 i
cnt[i] -= 1;
let ways = Self::count_arrangements(&cnt);
if k <= ways {
ans.push((b'a' + i as u8) as char);
break;
} else {
// 不在该分支,恢复并跳过
cnt[i] += 1;
k -= ways;
}
}
}

let rev: String = ans.chars().rev().collect();
ans + mid + &rev
}

/// 计算多重集排列数,超过 MAX_K 时截断
fn count_arrangements(cnt: &[i32; 26]) -> i64 {
let mut total = 0i32;
for &c in cnt.iter() {
total += c;
}
let mut res: i64 = 1;
for &c in cnt.iter() {
if c == 0 {
continue;
}
res *= Self::comb(total, c);
if res >= Self::MAX_K {
return Self::MAX_K;
}
total -= c;
}
res
}

/// 组合数 C(n, k),超过 MAX_K 时截断
fn comb(n: i32, k: i32) -> i64 {
let n = n as i64;
let k = k as i64;
let k = k.min(n - k);
let mut res: i64 = 1;
for i in 1..=k {
res = res * (n - i + 1) / i;
if res >= Self::MAX_K {
return Self::MAX_K;
}
}
res
}
}
```

关键点说明:
- `count_arrangements` 将多重集排列分解为依次选位置的组合数连乘,避免了先算大阶乘再除法的溢出问题。
- `comb` 利用 `C(n,k) = C(n,k-1) * (n-k+1) / k` 递推,并取 `k = min(k, n-k)` 减少循环次数,同时保证中间结果始终可被整除。
- 截断值 `MAX_K = 1_000_001` 略大于题目 `k` 的上限,既防溢出又不会影响正确判断。

http://www.jsqmd.com/news/1155146/

相关文章:

  • OBS虚拟摄像头终极指南:如何将OBS画面变成系统摄像头
  • TPA3128D2音频放大器与PIC18LF4458微控制器的集成方案
  • VisualCppRedist AIO终极指南:一键解决Windows软件兼容性问题的完整解决方案
  • C++容器精讲:从底层原理到高性能实战,掌握STL核心利器
  • 锂电池组电压均衡方案:MP2672A与MK20DX128VFM5应用
  • 4D毫米波雷达点云生成实战:基于U-Net与GAN的CFAR替代方案,点云密度提升3倍
  • 基于TC78H651AFNG的直流有刷电机驱动器设计与优化
  • 高压安全隔离技术:ISOM8710与STM32F302VC应用指南
  • CATIA 2020虚拟网卡安装失败的根源与精准修复方案
  • 终极免费Altium电路图查看器:无需专业软件解析SchDoc文件的完整指南
  • TPS61170升压转换器与MK64FX512VDC12 MCU协同设计指南
  • 终极指南:3步掌握drawio专业图标库,轻松绘制惊艳技术图表
  • Apache ActiveMQ 5.17.4 漏洞环境一键搭建:Docker Compose 3步复现 CVE-2023-46604
  • PyTorch Tensor 数据类型选择指南:FP16/BF16/FP32 在 RTX 4090 上的3倍速度对比
  • TMC7300与PIC18F4455驱动有刷直流电机方案解析
  • 基于TC78H651AFNG的高效直流有刷电机驱动方案
  • Trae AI编程实践:面向Java+Vue全栈开发者的智能协作工作台
  • Vue CLI 与 Vite 项目中 Sass/Less 混用实践:3个步骤解决第三方 UI 库样式覆盖
  • 对标“新双高”与“人工智能+教育”:未来实训中心如何落地
  • 饥荒 Mod 开发:F5/F6 键实现手动存档/读档,覆盖 2 种游戏场景
  • STM32F427ZI与CMT-8540S-SMT嵌入式音频开发实战
  • STM32 DS18B20温度采集优化:3种滤波算法对比与±0.1℃精度实现
  • 微信自动回复开发的两大Python防线
  • 基于MK64FN1M0VDC12与MCP3202的锂电池电压平衡系统设计
  • 工业负载控制:智能功率驱动器(IPD)选型与应用
  • OpenProject:如何用开源方案解决企业级项目管理的五大核心痛点
  • 东芝TC78H651AFNG与PIC18F4458的直流电机驱动方案
  • Cherry MX键帽3D模型库:打造你的专属机械键盘设计指南
  • 谷歌浏览器国内直连下载地址
  • 企业级GEO服务选型与采购白皮书 ——2026年企业AI时代品牌心智占位的实战指南