[LeetCode] 1312. Minimum Insertion Steps to Make a String Palindrome 让字符串成为回文串的最少插入次数
Given a string s
. In one step you can insert any character at any index of the string.
Return the minimum number of steps to make s
palindrome.
A Palindrome String is one that reads the same backward as well as forward.
Example 1:
Input: s = "zzazz"
Output: 0
Explanation: The string "zzazz" is already palindrome we don't need any insertions.
Example 2:
Input: s = "mbadm"
Output: 2
Explanation: String can be "mbdadbm" or "mdbabdm".
Example 3:
Input: s = "leetcode"
Output: 5
Explanation: Inserting 5 characters the string becomes "leetcodocteel".
Constraints:
1 <= s.length <= 500
s
consists of lowercase English letters.
这道题说是给了一个字符串s,可以在任意位置加入任意字符,问可以使其变为回文串的最少添加字符的个数是多少。对于回文串,想必大家都不陌生,就是正读反读都一样的字符串,比如 noon, bob 等等。这里给定的字符串如果本身就是个回文串,则就不需要加入字符了,比如例子1,若不是回文串,就需要在适当的位置加入字符,使其变为回文串,比如例子2。对于字符串求极值的问题,经验常识告诉我们基本上都是用动态规划 Dynamic Programming 来做,这里如果直接去想到底在哪个位置上加字符才能使其变为回文串,感觉不太好入手,因为可以加的方法太多了,总不能遍历所有的情况吧。
这里需要转变个思路来做,可以说这个思路转变是这道题的精髓所在,在直接给出答案之前,让我们先来做个简单的分析:由于可以在任意位置加上任意个字符,所以只要把原字符串反转一下,直接加在后面,就一定是一个回文串,但是这种加法不一定用的字符最少,其实应该尽可能去利用原字符串里具有的一些子序列回文串,比如例子2的 mbadm
,如果利用其子序列回文串 mam
的话,实际上只需要增添b和d两个字符就可以构成整体的回文串了。所以子序列回文串越长,需要添加的字符就越少,于是乎问题就转换为了求最长的子序列回文串的长度,就变成了之前的那道 Longest Palindromic Subsequence。
这里定义一个二维的 dp 数组,其中 dp[i][j] 表示原字符串 [i, j] 范围内的子串中最长子序列回文串的长度。接下来就要找状态转移方程了,先从最简单的开始分析,若子串长度为1,则其一定是回文串,即 dp[i][i] 为1,然后以此为中心,分别往两边扩展,比较两边相对应的字符是否相同,对于一个区间 [i, j],两边的字符分别为 s[i] 和 s[j],中间的部分是 [i+1, j-1],假设中间子串中的最长子序列回文串的长度已经求得了,为 dp[i+1][j-1],若此时 s[i] 和 s[j] 相等的话,说明子序列回文串的长度又增加了2,则可以用 dp[i+1][j-1] + 2
来更新 dp[i][j],若 s[i] 和 s[j] 不相等的话,则可以比较区间 [i+1, j] 和 [i, j-1] 中谁的子序列回文串更长,用较长的长度来更新 dp[i][j]。分析到这里,代码就不难写了吧,最终字符串s的最长子序列回文串的长度为 dp[0][n-1],只要返回 n - dp[0][n-1]
即使要增加的字符个数了,参见代码如下:
class Solution {
public:
int minInsertions(string s) {
int res = 0, n = s.size();
vector<vector<int>> dp(n, vector<int>(n));
for (int i = n - 1; i >= 0; --i) {
dp[i][i] = 1;
for (int j = i + 1; j < n; ++j) {
dp[i][j] = (s[i] == s[j]) ? (dp[i + 1][j - 1] + 2) : max(dp[i + 1][j], dp[i][j - 1]);
}
}
return n - dp[0][n - 1];
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1312
类似题目:
Longest Palindromic Subsequence
Minimum Number of Moves to Make Palindrome
参考资料:
https://leetcode.com/problems/minimum-insertion-steps-to-make-a-string-palindrome/