题目描述
给你一个字符串 s
,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s
成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。
示例 1:
输入:s = "zzazz" 输出:0 解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。
示例 2:
输入:s = "mbadm" 输出:2 解释:字符串可变为 "mbdadbm" 或者 "mdbabdm" 。
示例 3:
输入:s = "leetcode" 输出:5 解释:插入 5 个字符后字符串变为 "leetcodocteel" 。
提示:
1 <= s.length <= 500
s
中所有字符都是小写字母。
解题思路
class Solution {
public:
int minInsertions(string s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n));
for (int span = 2; span <= n; span++) {
for (int i = 0; i + span <= n; i++) {
int j = i + span - 1;
dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
if (i <= j && s[i] == s[j]) {
dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
}
}
}
return dp[0][n - 1];
}
};
函数解析
- 函数原型:
int minInsertions(string s)
- 参数:
s
是需要处理的字符串。 - 返回值:返回将字符串
s
转换成回文字符串所需的最小插入次数。
- 参数:
动态规划表达式解析
- 定义状态:
dp[i][j]
表示使子字符串s[i...j]
变成回文所需的最小插入次数。
代码逻辑
-
初始化:字符串的长度
n
被用来初始化一个二维动态规划数组dp
。dp
中的每个元素被初始化为 0。由于字符串长度为 1 的子字符串本身就是回文,所以dp[i][i]
自然为 0。 -
填充 DP 表:
- 外层循环:通过变量
span
从 2 到n
(包括整个字符串的长度)来控制考虑的子字符串长度。 - 内层循环:通过变量
i
来遍历字符串,计算所有长度为span
的子字符串。j
为子字符串的结束索引,计算公式为i + span - 1
。
- 状态转移:
- 如果
s[i]
和s[j]
相等,说明两端匹配,可以考虑缩小问题的规模到dp[i + 1][j - 1]
,即不需要在两端插入字符。 - 如果不相等,则考虑两种情况:在
i
之后或j
之前插入字符,使得字符串更接近回文,即取dp[i + 1][j]
和dp[i][j - 1]
中的较小值,并加一(表示插入操作)。
- 如果
- 外层循环:通过变量
-
返回结果:最后
dp[0][n - 1]
存储的是将整个字符串s
转换为回文字符串所需的最小插入次数。