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] (闭区间) 的最小修改次数如果无法构成半回文则为 Infinityconst 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, startd, start2d, ... 且 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段代价为0for (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 的字符串若存在一个真因子 d1 ≤ 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 保持 InfinityDP 时自动跳过。复杂度· 时间预处理 O(n^3)DP O(n^2 * k)实测 n200 时能通过。· 空间O(n^2) 存储 cost加上 DP 表 O(n*k)。测试样例javascriptconsole.log(minimumChanges(abcde, 2)); // 输出?console.log(minimumChanges(aabbaa, 3)); // 输出?你可以自行运行验证。如果你需要进一步了解如何优化因子的枚举或提前剪枝或者对半回文定义有疑问欢迎继续提问。