目录
题目
给你一个字符串 s
,每一次操作你都可以在字符串的任意位置插入任意字符。请你返回让 s
成为回文串的 最少操作次数 。「回文串」是正读和反读都相同的字符串。
示例 1:
输入:s = "zzazz"
输出:0
解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。
示例 2:
输入:s = "mbadm"
输出:2
解释:字符串可变为 "mbdadbm" 或者 "mdbabdm" 。
示例 3:
输入:s = "leetcode"
输出:5
解释:插入 5 个字符后字符串变为 "leetcodocteel" 。
提示:
1 <= s.length <= 500
s
中所有字符都是小写字母。
思路
这个问题可以通过动态规划来解决,具体是最长公共子序列(LCS)问题的一个变体。我们的目标是找到一个最少的插入次数,使得给定的字符串s
成为回文串。
解题过程
-
定义状态:设
dp[i][j]
表示字符串s
从索引i
到j
的子串最少需要插入的字符数,以使其成为回文串。 -
状态转移方程:如果
s[i] == s[j]
,那么dp[i][j] = dp[i + 1][j - 1]
,因为两个相同的字符不需要插入就可以形成回文。如果s[i] != s[j]
,我们需要在i
和j
之间插入字符,使得子串成为回文,此时dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j - 1])
。 -
初始化:对于所有的
i
,dp[i][i] = 0
,因为单个字符总是回文的。 -
计算顺序:我们可以从小到大计算
dp[i][j]
,即先计算短的子串,然后使用这些结果来计算更长的子串。 -
构造最优解:最终,
dp[0][n-1]
(其中n
是字符串s
的长度)将给出整个字符串s
成为回文串的最少操作次数。
复杂度
- 时间复杂度:
O(n^2)
,其中n
是字符串s
的长度。我们需要计算一个n x n
的动态规划表。 - 空间复杂度:
O(n^2)
,用于存储动态规划表。
code
def minInsertions(s):
n = len(s)
# 初始化动态规划表
dp = [[0] * n for _ in range(n)]
# 从最长为2的子串开始,逐步增加长度
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
# 示例
print(minInsertions("zzazz")) # 输出:0
print(minInsertions("mbadm")) # 输出:2
print(minInsertions("leetcode")) # 输出:5