合并字符串形成回文串的最小插入成本问题
题目描述
给你一个字符串 s,你可以执行以下操作任意次:
- 在字符串的任意位置插入一个小写英文字母,每次插入的成本是固定的,但不同字符的插入成本可能不同(题目会给出每个字符的插入成本表)。
问:最少需要多少总插入成本,才能将原字符串 s 变成回文串?
示例
假设字符串 s = "abca",字母插入成本为:a:1, b:2, c:3。
一种方法:在开头插入 'a',得到 "aabca",总成本 = 1(插入 'a' 的成本)。
另一种方法:在末尾插入 'c',得到 "abcac",总成本 = 3。
最优解:在末尾插入 'a',得到 "abcba"(需先处理对称性),实际需要插入 2 个字符?我们下面会分析。
实际上,我们可以通过动态规划计算最小成本,而不是直观猜测。
为什么这是区间 DP 问题
要将整个字符串变成回文,我们可以从两边向中间考虑:
- 如果
s[i] == s[j],那么这两个字符已经配对,不需要在它们之间插入新字符来匹配它们,只需考虑内部子串(i+1, j-1)变成回文的最小成本。 - 如果
s[i] != s[j],有两种方式让它们匹配:- 在
j的右边插入一个字符等于s[i],成本是cost[s[i]],然后问题变成子串(i+1, j)变成回文。 - 在
i的左边插入一个字符等于s[j],但注意在 DP 中,我们可视为“在子串(i, j-1)的右边插入s[j]”,成本是cost[s[j]],然后问题变成子串(i, j-1)变成回文。
- 在
这样,状态就只依赖于更小的区间,符合区间 DP 的特征。
动态规划定义
定义 dp[i][j] 为将子串 s[i..j] 变成回文串的最小插入成本。
- 当
i > j时,子串为空,已经是回文,成本 0。 - 当
i == j时,单个字符,已经是回文,成本 0。
转移方程:
- 如果
s[i] == s[j]:- 如果
j == i+1,即两个相同字符相邻,已经是回文,成本 0。 - 否则,
dp[i][j] = dp[i+1][j-1](因为这两个字符已经匹配,只需处理中间部分)。
- 如果
- 如果
s[i] != s[j]:- 选择 1:在
j右边插入s[i],成本 =cost[s[i]] + dp[i+1][j]。 - 选择 2:在
i左边插入s[j],成本 =cost[s[j]] + dp[i][j-1]。 - 取两者最小值。
- 选择 1:在
即:
\[dp[i][j] = \begin{cases} dp[i+1][j-1] & \text{if } s[i] = s[j] \\ \min(cost[s[i]] + dp[i+1][j],\ cost[s[j]] + dp[i][j-1]) & \text{if } s[i] \neq s[j] \end{cases} \]
最终答案:dp[0][n-1],其中 n 是字符串长度。
逐步示例计算
设 s = "abca",cost[a]=1, cost[b]=2, cost[c]=3。
n = 4,下标 0~3。
初始化:dp[i][i] = 0 对所有 i。
按区间长度 len 从 2 到 4 计算:
len=2:
-
[0,1]: s[0]=a, s[1]=b 不同
dp[0][1] = min(cost[a]+dp[1][1], cost[b]+dp[0][0])
= min(1+0, 2+0) = 1(在 b 右边插入 a 成本更低) -
[1,2]: s[1]=b, s[2]=c 不同
= min(cost[b]+dp[2][2], cost[c]+dp[1][1])
= min(2+0, 3+0) = 2 -
[2,3]: s[2]=c, s[3]=a 不同
= min(3+0, 1+0) = 1
len=3:
-
[0,2]: s[0]=a, s[2]=c 不同
= min(cost[a]+dp[1][2], cost[c]+dp[0][1])
= min(1+2, 3+1) = min(3,4) = 3 -
[1,3]: s[1]=b, s[3]=a 不同
= min(cost[b]+dp[2][3], cost[a]+dp[1][2])
= min(2+1, 1+2) = min(3,3) = 3
len=4:
- [0,3]: s[0]=a, s[3]=a 相同
dp[0][3] = dp[1][2] = 2
最终结果:dp[0][3] = 2。
验证:原串 "abca" 如何以成本 2 变回文?
- 在末尾插入 'b'(成本 2),得到 "abcab",是回文吗?不是。
我们仔细看 dp 过程:实际上最优是:
看 [0,3] 相同,用 [1,2] 的结果 2,表示里面 "bc" 变回文成本 2。
"bc" 变回文:在 c 右边插入 b(成本 2),得到 "bcb",所以整体是 "a"+"bcb"+"a" = "abcba",成本=2。
对,这就是最优解。
复杂度与实现细节
- 时间复杂度:O(n²),状态数 n²,转移 O(1)。
- 空间复杂度:O(n²),可优化到 O(n) 用滚动数组,但通常直接二维数组即可。
实现要点:
- 外层循环枚举区间长度 len 从 2 到 n。
- 内层循环枚举起点 i,计算 j = i+len-1。
- 注意当 s[i]==s[j] 时,如果 j=i+1,则 dp[i][j]=0,这被包含在 dp[i+1][j-1] 中,因为 dp[i+1][j-1] 中 i+1>j-1 时返回 0,但代码中通常直接处理:
- 若 j==i+1 且相同,dp[i][j]=0。
- 否则 dp[i][j]=dp[i+1][j-1]。
核心思想总结
这个区间 DP 问题本质是“通过插入字符使字符串回文”,与经典的“最小插入次数”问题相似,但每次插入成本不同。
- 当两端字符相等时,直接由内层子问题得到。
- 当两端不等时,选择成本更小的字符插入来匹配另一端,然后转化为长度减 1 的子问题。
这样,我们就能在多项式时间内求出最小成本,而不需要枚举所有可能的插入位置与字符。