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

DeepSeek LeetCode 2911. 得到 K 个半回文串的最少修改次数 JavaScript实现

由于你再次询问同一题,我会提供一份更注重可读性 + 优化细节的 JavaScript 实现,并附上详细的思路注释,帮助你彻底理解半回文串的处理方式。

```javascript
/**
* 2911. 得到 K 个半回文串的最少修改次数
* @param {string} s
* @param {number} k
* @return {number}
*/
var minimumChanges = function(s, k) {
const n = s.length;

// ---------- 1. 预处理所有子串变成半回文的最小修改次数 ----------
// cost[l][r] 表示 s[l..r] (闭区间) 的最小修改次数,如果无法构成半回文则为 Infinity
const cost = Array.from({ length: n }, () => new Array(n).fill(Infinity));

// 枚举所有子串
for (let l = 0; l < n; l++) {
for (let r = l; r < n; r++) {
const len = r - l + 1;
if (len < 2) continue; // 长度至少为2才能成为半回文

let best = Infinity;
// 枚举 len 的所有真因子 d (1 <= d < len 且 len % d == 0)
// 注意:d = len 排除,因为要求真因子
for (let d = 1; d < len; d++) {
if (len % d !== 0) continue;

let changes = 0;
// 对于每个起始偏移 start (0 ~ d-1)
for (let start = 0; start < d; start++) {
// 收集该轨的所有下标: start, start+d, start+2d, ... 且 <= r
// 使用双指针从两端往中间配对
let i = l + start;
let j = l + start + Math.floor((len - 1 - start) / d) * d;
while (i < j) {
if (s[i] !== s[j]) changes++;
i += d;
j -= d;
}
}
best = Math.min(best, changes);
}
cost[l][r] = best;
}
}

// ---------- 2. 动态规划:将字符串分成 k 段 ----------
// dp[i][c] : 将前 i 个字符 (s[0..i-1]) 分成 c 段的最少修改次数
const dp = Array.from({ length: n + 1 }, () => new Array(k + 1).fill(Infinity));
dp[0][0] = 0; // 空串分成0段代价为0

for (let c = 1; c <= k; c++) { // 枚举段数
for (let i = c; i <= n; i++) { // 至少需要 c 个字符
// 枚举最后一段的起始位置 j (前 j 个字符已经分成了 c-1 段)
for (let j = c - 1; j < i; j++) {
if (dp[j][c - 1] !== Infinity && cost[j][i - 1] !== Infinity) {
dp[i][c] = Math.min(dp[i][c], dp[j][c - 1] + cost[j][i - 1]);
}
}
}
}

return dp[n][k];
};
```

解题核心要点

1. 半回文的判断与最小修改

· 定义:长度为 L 的字符串,若存在一个真因子 d(1 ≤ d < L 且 d | L),使得将所有下标按 mod d 分组后,每组都是一个回文串,则称该字符串是半回文。
· 修改次数计算:
· 对于每个 d,将原串映射成 d 条轨道。
· 每条轨道内的字符必须回文,所以统计轨道内对称位置不同的对数。
· 所有轨道的总不同对数就是按此 d 划分需要的修改次数。
· 取所有真因子 d 中的最小值。

2. 为什么预处理是必要的?

如果直接在 DP 里对每个子串实时计算半回文代价,会重复计算大量子串,复杂度爆炸。预处理 O(n^3) 在 n ≤ 200 时可行(约 800 万次操作),DP 额外 O(n^2 * k) 也 ok。

3. 动态规划细节

· 状态 dp[i][c]:前 i 个字符分成 c 段的最小代价。
· 转移:枚举最后一段的开始位置 j(前 j 个字符分成 c-1 段),加上子串 s[j..i-1] 的半回文代价。
· 初始化 dp[0][0] = 0,其他为无穷大。
· 答案 = dp[n][k]。

4. 边界条件

· 若 k > n 理论上无法划分(题目不会出现)。
· 若某个子串无法变成半回文(如长度 1),则 cost 保持 Infinity,DP 时自动跳过。

复杂度

· 时间:预处理 O(n^3),DP O(n^2 * k),实测 n=200 时能通过。
· 空间:O(n^2) 存储 cost,加上 DP 表 O(n*k)。

测试样例

```javascript
console.log(minimumChanges("abcde", 2)); // 输出:?
console.log(minimumChanges("aabbaa", 3)); // 输出:?
```

你可以自行运行验证。

如果你需要进一步了解如何优化因子的枚举或提前剪枝,或者对半回文定义有疑问,欢迎继续提问。

http://www.zskr.cn/news/1445440.html

相关文章:

  • 道本科技与DeepSeek联合解决方案:助力国央企合同管理数字化转型升级白皮书
  • 第31篇 k8s之Ingress 进阶:TLS、重写与认证
  • DevSecOps建设之移动端自动化技能Appium
  • 手把手教你用SAM模型处理CHAOS医学CT图像:从DCM到NPZ的完整预处理流程
  • 3分钟搞定NVIDIA显卡色彩校准:让宽色域显示器回归真实色彩
  • 可重启序列:多核微处理器性能提升利器,最高让性能提升百万倍!
  • 7-7. 开题报告等文档资料学校会查重吗?
  • AI 编程浪潮下,Zig 等开源项目为何坚守「拒绝 AI 代码」?
  • 数字信任技术全景:从密码学基础到隐私保护实战
  • 用Python动手推导:能量守恒、勾股定理与机器学习损失函数之间的奇妙联系
  • 快放≠质量牺牲!Sora 2 v2.3实测数据:启用motion-aware upsampling后PSNR提升11.6dB,延迟降低43%
  • Java 集成 LibreOffice 实现离线文档转换:Windows 与 Linux 环境详解
  • Iinux:网络编程
  • 当样本量太小怎么办?Fisher精确检验实战指南(附SPSS操作避坑点)
  • 从OpenCLIP到Qwen-7B:手把手拆解Qwen-VL的视觉-语言对齐‘三明治’架构
  • AI 编程大势下,Zig 等开源项目为何坚决拒绝 AI 代码贡献?
  • 深入大模型-42-大模型交互之前端代码详解JavaScript代码
  • 基于Azure云平台的海量多媒体智能检索系统架构与实践
  • 别再只跑Demo了!Grounding DINO实战:用你自己的数据集做Fine-tuning(附完整代码)
  • 上电后MCU从哪开始执行?深入解析工业采集卡的BOOT启动配置电路
  • 如何打造高效AI研究周报:从信息筛选到团队洞察的完整指南
  • 我为什么要使用Ollama配置通义千问大模型
  • 别再混淆了!一文讲透STM32的UART、TTL、RS232、RS485和MODBUS协议关系
  • Debugger Canvas:可视化调试如何革新代码调试的认知模式
  • Win10开机报No Bootable Device别慌!从拍打到重装,我试了这5种方法(附详细命令)
  • 36小时打造AR内容推荐引擎:从PWA到向量检索的实战解析
  • UE5新手避坑指南:手把手教你开启Lumen全局光照,告别漫长的光照烘焙
  • LangChain4j AiServices 机制详解:快速构建智能体应用
  • 从Grudin定律到协同设计:人机交互与CSCW的核心思想与实践
  • 用STM32F103C8T6和AD9850自制高精度信号发生器,从电路焊接、代码编写到波形测试全流程避坑