线性动态规划:最小插入次数构造回文串(进阶版——允许删除操作)
字数 1559 2025-11-29 01:35:50
线性动态规划:最小插入次数构造回文串(进阶版——允许删除操作)
题目描述
给定一个字符串 s,你可以进行三种操作:插入一个字符、删除一个字符或替换一个字符(每次操作代价均为 1)。要求通过一系列操作将 s 转换为回文串,并返回最小操作次数。注意:替换操作允许将字符替换为任意字符。
解题思路
此问题可视为编辑距离问题的变种,目标是将字符串转换为回文串。定义动态规划状态 dp[i][j] 表示将子串 s[i:j](闭区间)转换为回文串所需的最小操作次数。通过比较首尾字符,逐步向中间收缩,分类讨论操作策略。
详细步骤
-
状态定义
- 设
dp[i][j]表示使子串s[i...j]成为回文的最小操作次数(0 ≤ i ≤ j < n)。
- 设
-
边界条件
- 当
i == j:单字符本身就是回文,操作次数为 0。 - 当
i > j:空串视为回文,操作次数为 0。
- 当
-
状态转移方程
考虑子串s[i...j]:- 情况1:若
s[i] == s[j],两端字符已匹配,无需操作两端,直接处理内部子串:
dp[i][j] = dp[i+1][j-1]。 - 情况2:若
s[i] != s[j],需通过操作使两端匹配,有三种选择:- 插入:在
j后插入s[i],使s[i]与新增字符匹配,然后处理s[i+1...j]:
dp[i][j] = dp[i+1][j] + 1。 - 删除:删除
s[i],然后处理s[i+1...j]:
dp[i][j] = dp[i+1][j] + 1。 - 替换:将
s[j]替换为s[i](或反之),使两端匹配,然后处理s[i+1...j-1]:
dp[i][j] = dp[i+1][j-1] + 1。
取三种操作的最小值:
dp[i][j] = min(dp[i+1][j], dp[i][j-1], dp[i+1][j-1]) + 1。
- 插入:在
- 情况1:若
-
遍历顺序
由于dp[i][j]依赖i+1和j-1(即左下方、下方、左方的状态),需从子串长度由小到大遍历:- 外层循环枚举子串长度
len从2到n。 - 内层循环枚举起始下标
i从0到n-len,计算j = i+len-1。
- 外层循环枚举子串长度
-
最终答案
返回dp[0][n-1],即整个字符串的最小操作次数。
示例演示
以 s = "abca" 为例:
- 初始化:单字符子串操作次数为 0。
- 长度 2:
"ab":s[0]!=s[1],dp[0][1] = min(dp[1][1], dp[0][0], dp[1][0])+1 = min(0,0,0)+1 = 1。"bc":同理得dp[1][2] = 1。"ca":dp[2][3] = 1。
- 长度 3:
"abc":s[0]!=s[2],dp[0][2] = min(dp[1][2], dp[0][1], dp[1][1])+1 = min(1,1,0)+1 = 1。"bca":s[1]!=s[3],dp[1][3] = min(dp[2][3], dp[1][2], dp[2][2])+1 = min(1,1,0)+1 = 1。
- 长度 4:
"abca":s[0]==s[3],dp[0][3] = dp[1][2] = 1。
最终结果为1,例如将'b'替换为'c'得到"acca"。
复杂度分析
- 时间复杂度:O(n²),需填充 n×(n+1)/2 个状态。
- 空间复杂度:O(n²),可优化至 O(n)(仅保留当前行和上一行)。