题目描述
给你一个字符串 s
,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s
成为回文串的 最少操作次数。
回文串 是正读和反读都相同的字符串。
样例
输入:s = "zzazz"
输出:0
解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。
输入:s = "mbadm"
输出:2
解释:字符串可变为 "mbdadbm" 或者 "mdbabdm"。
输入:s = "leetcode"
输出:5
解释:插入 5 个字符后字符串变为 "leetcodocteel"。
输入:s = "g"
输出:0
输入:s = "no"
输出:1
限制
1 <= s.length <= 500
s
中所有字符都是小写字母。
算法
(动态规划) $O(n^2)$
- 设状态 $f(i, j)$ 表示将区间
[i, j]
变为回文串的最少操作次数。 - 初始时,$f(i, i) = 0$。如果
s[i] == s[i + 1]
,则 $f(i, i + 1) = 0$;否则 $f(i, i + 1) = 1$。 - 转移时,先枚举长度,然后处理这个长度下的所有状态。对于区间 $[i, j]$,如果
s[i] == s[j]
,则 $f(i, j) = f(i + 1, j - 1)$;否则 $f(i, j) = \min(f(i + 1, j), f(i, j - 1)) + 1$,即需要匹配第一个字符或者最后一个字符。 - 最终答案为 $f(0, n - 1)$。
时间复杂度
- 状态数共有 $O(n^2)$,每次转移仅需要常数时间,故总时间复杂度为 $O(n^2)$。
空间复杂度
- 需要额外 $O(n^2)$ 的空间存储状态。
C++ 代码
class Solution {
public:
int minInsertions(string s) {
int n = s.length();
vector<vector<int>> f(n, vector<int>(n));
for (int i = 0; i < n; i++)
f[i][i] = 0;
for (int i = 0; i < n - 1; i++)
f[i][i + 1] = (int)(!(s[i] == s[i + 1]));
for (int len = 3; len <= n; len++)
for (int i = 0; i < n - len + 1; i++) {
int j = i + len - 1;
if (s[i] == s[j])
f[i][j] = f[i + 1][j - 1];
else
f[i][j] = min(f[i + 1][j], f[i][j - 1]) + 1;
}
return f[0][n - 1];
}
};