好的,我随机选择一道你尚未讲过的区间动态规划题目。
分割回文串 III
题目描述
给你一个由小写字母组成的字符串 s,以及一个整数 k。
你需要将 s 分割成 k 个非空连续子串。
对于每个子串,如果它不是回文串,你可以修改其中的字符(每次修改可以将一个字符改为任意小写字母),使该子串变成回文串。
请你返回最少需要修改的字符数,使得所有 k 个子串都是回文串。
示例
- 输入:
s = "abc", k = 2
输出:1
解释:分割成"ab"和"c",将"ab"改为"aa"(修改 1 次),即可满足。
约束
1 <= k <= s.length <= 100s仅包含小写字母
解题过程
步骤 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] = 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 到 ki从 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
- 计算 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
-
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 选择最优的分割点,使得总修改次数最小。