分割回文串 III
字数 1864 2025-12-16 04:19:34

好的,我随机选择一道你尚未讲过的区间动态规划题目。

分割回文串 III


题目描述

给你一个由小写字母组成的字符串 s,以及一个整数 k
你需要将 s 分割成 k非空连续子串
对于每个子串,如果它不是回文串,你可以修改其中的字符(每次修改可以将一个字符改为任意小写字母),使该子串变成回文串。
请你返回最少需要修改的字符数,使得所有 k 个子串都是回文串。

示例

  • 输入:s = "abc", k = 2
    输出:1
    解释:分割成 "ab""c",将 "ab" 改为 "aa"(修改 1 次),即可满足。

约束

  • 1 <= k <= s.length <= 100
  • s 仅包含小写字母

解题过程

步骤 1:问题拆解与思路

我们有两个任务:

  1. s 分成 k 个连续非空子串。
  2. 对每个子串,计算它变成回文串的最少修改次数
  3. 目标:所有子串修改次数之和最小。

区间动态规划适合解决这种分割型问题,因为分割点位置会影响子串的修改代价。


步骤 2:预处理子串修改代价

cost[i][j] 表示将子串 s[i:j](左闭右闭,即下标从 ij)变成回文串的最少修改次数。
如何计算 cost[i][j]

  • 对于子串 s[i..j],要变成回文,需要 s[i]s[j] 相等,否则修改其中一个,计数一次。
  • 如果长度 ≤ 1,本身就是回文,修改次数为 0。
  • 可以用动态规划或直接双指针计算:
    从两端向中间比对,不同则计数 +1。

转移方程

cost[i][j] = cost[i+1][j-1] + (s[i] != s[j])

这里 cost[i][j] 可以通过递推得到,从短区间向长区间计算。

示例s = "abc"

  • cost[0][2]:比较 'a''c',不同,所以看中间 "b",长度为 1,cost[1][1]=0,因此 cost[0][2] = 0 + 1 = 1。

实现细节

n = len(s)
cost = [[0]*n for _ in range(n)]
for length in range(2, n+1):
    for i in range(0, n-length+1):
        j = i+length-1
        if length == 2:
            cost[i][j] = 0 if s[i]==s[j] else 1
        else:
            cost[i][j] = cost[i+1][j-1] + (0 if s[i]==s[j] else 1)

注意:当 length=1 时,cost[i][i]=0,初始化已默认。


步骤 3:定义主 DP 状态

dp[p][i] 表示将 s[0..i](前 i+1 个字符)分割成 p 个子串的最少修改次数

  • p 从 1 到 k
  • i 从 0 到 n-1

初始条件

  • p=1 时,dp[1][i] = cost[0][i](前 i+1 个字符作为一个子串的修改代价)

步骤 4:状态转移

要计算 dp[p][i],考虑最后一个子串的起始位置 j

  • p-1 个子串覆盖 s[0..j-1],最后一个子串是 s[j..i]
  • 则总代价 = dp[p-1][j-1] + cost[j][i]
  • 枚举所有可能的 j(即最后一个子串的起点),取最小值。

转移方程

dp[p][i] = min{ dp[p-1][j-1] + cost[j][i] },其中 p-1 <= j <= i

注意:j 必须 ≥ p-1(因为前 p-1 个子串至少需要 p-1 个字符),且 j ≥ 1(因为前一部分至少有一个字符)。
p=1 时直接由 cost 得到。


步骤 5:最终答案

dp[k][n-1] 即为将整个字符串分成 k 个子串的最小修改次数。


步骤 6:示例推演

s = "abc", k = 2

  1. 计算 cost:
cost[0][0]=0, cost[1][1]=0, cost[2][2]=0
cost[0][1] = (a!=b) -> 1
cost[1][2] = (b!=c) -> 1
cost[0][2] = cost[1][1] + (a!=c) = 0+1 = 1
  1. dp 表初始化(p=1):
    dp[1][0] = cost[0][0] = 0
    dp[1][1] = cost[0][1] = 1
    dp[1][2] = cost[0][2] = 1

  2. 计算 p=2:
    dp[2][1]:i=1,分割成 2 个子串,枚举 j=1(前 1 个子串覆盖 s[0..0],后一个覆盖 s[1..1]):
    dp[1][0] + cost[1][1] = 0+0=0
    所以 dp[2][1] = 0

dp[2][2]:i=2,枚举 j=1 或 2

  • j=1:dp[1][0]+cost[1][2] = 0+1=1
  • j=2:dp[1][1]+cost[2][2] = 1+0=1
    取 min = 1

所以最终 dp[2][2] = 1,答案 1。


步骤 7:复杂度分析

  • 预处理 cost:O(n²)
  • dp 计算:O(k·n²)
    总复杂度 O(k·n²),n ≤ 100,可行。

总结

这道题是区间 DP + 分割型 DP 的结合:

  1. 用区间 DP 预处理任意子串变回文的最小修改次数。
  2. 用分割 DP 选择最优的分割点,使得总修改次数最小。
好的,我随机选择一道你尚未讲过的区间动态规划题目。 分割回文串 III 题目描述 给你一个由小写字母组成的字符串 s ,以及一个整数 k 。 你需要将 s 分割成 k 个 非空连续子串 。 对于每个子串,如果它不是回文串,你可以 修改其中的字符 (每次修改可以将一个字符改为任意小写字母),使该子串变成回文串。 请你返回 最少需要修改的字符数 ,使得所有 k 个子串都是回文串。 示例 输入: s = "abc", k = 2 输出: 1 解释:分割成 "ab" 和 "c" ,将 "ab" 改为 "aa" (修改 1 次),即可满足。 约束 1 <= k <= s.length <= 100 s 仅包含小写字母 解题过程 步骤 1:问题拆解与思路 我们有两个任务: 将 s 分成 k 个连续非空子串。 对每个子串,计算它变成回文串的 最少修改次数 。 目标:所有子串修改次数之和最小。 区间动态规划适合解决这种 分割型 问题,因为分割点位置会影响子串的修改代价。 步骤 2:预处理子串修改代价 设 cost[i][j] 表示将子串 s[i:j] (左闭右闭,即下标从 i 到 j )变成回文串的最少修改次数。 如何计算 cost[i][j] ? 对于子串 s[i..j] ,要变成回文,需要 s[i] 与 s[j] 相等,否则修改其中一个,计数一次。 如果长度 ≤ 1,本身就是回文,修改次数为 0。 可以用动态规划或直接双指针计算: 从两端向中间比对,不同则计数 +1。 转移方程 : 这里 cost[i][j] 可以通过递推得到,从短区间向长区间计算。 示例 : s = "abc" cost[0][2] :比较 'a' 与 'c' ,不同,所以看中间 "b" ,长度为 1,cost[ 1][ 1]=0,因此 cost[ 0][ 2 ] = 0 + 1 = 1。 实现细节 : 注意:当 length=1 时,cost[ i][ i ]=0,初始化已默认。 步骤 3:定义主 DP 状态 设 dp[p][i] 表示将 s[0..i] (前 i+1 个字符)分割成 p 个子串的 最少修改次数 。 p 从 1 到 k i 从 0 到 n-1 初始条件 : p=1 时, dp[1][i] = cost[0][i] (前 i+1 个字符作为一个子串的修改代价) 步骤 4:状态转移 要计算 dp[p][i] ,考虑最后一个子串的起始位置 j : 前 p-1 个子串覆盖 s[0..j-1] ,最后一个子串是 s[j..i] 。 则总代价 = dp[p-1][j-1] + cost[j][i] 枚举所有可能的 j (即最后一个子串的起点),取最小值。 转移方程 : 注意: j 必须 ≥ p-1(因为前 p-1 个子串至少需要 p-1 个字符),且 j ≥ 1(因为前一部分至少有一个字符)。 当 p=1 时直接由 cost 得到。 步骤 5:最终答案 dp[k][n-1] 即为将整个字符串分成 k 个子串的最小修改次数。 步骤 6:示例推演 s = "abc", k = 2 计算 cost: dp 表初始化(p=1): dp[1][0] = cost[0][0] = 0 dp[1][1] = cost[0][1] = 1 dp[1][2] = cost[0][2] = 1 计算 p=2: dp[2][1] :i=1,分割成 2 个子串,枚举 j=1(前 1 个子串覆盖 s[ 0..0],后一个覆盖 s[ 1..1 ]): dp[1][0] + cost[1][1] = 0+0=0 所以 dp[ 2][ 1 ] = 0 dp[2][2] :i=2,枚举 j=1 或 2 j=1:dp[ 1][ 0]+cost[ 1][ 2 ] = 0+1=1 j=2:dp[ 1][ 1]+cost[ 2][ 2 ] = 1+0=1 取 min = 1 所以最终 dp[ 2][ 2 ] = 1,答案 1。 步骤 7:复杂度分析 预处理 cost:O(n²) dp 计算:O(k·n²) 总复杂度 O(k·n²),n ≤ 100,可行。 总结 这道题是 区间 DP + 分割型 DP 的结合: 用区间 DP 预处理任意子串变回文的最小修改次数。 用分割 DP 选择最优的分割点,使得总修改次数最小。