刚做过编辑距离的,我就想直接复制一个反转字符串,然后这两个字符串相同,前几个还可以的,但是到“leetcode”这个时,就不行了。仔细想这两个问题还是不等价,72题中的编辑距离中的操作包括更改操作,但是本题中的操作只有插入,所以果然不行。
然后我又想从两边向中间靠拢,leetcode倒是通过了,第18个案例"zjveiiwvc"就通不过了
class Solution {
public:
int minInsertions(string s) {
int n=s.size();
if(n<2) return 0;
int dp[n+2],low=1,high=n;
memset(dp,0,sizeof(dp));
while(low<high)
{
if(s[low-1]==s[high-1])
{
dp[low]=dp[low-1];
++low;--high;}
else
{
dp[low]=dp[low-1]+1;
++low;}
}
return dp[low-1];
}
};
然后我想是不是要从中间到两边,而中间的选定是要从0-(n-1)遍历一遍。然后我发现会出现偶数序列和奇数序列问题,再然后想到最长回文子串也出现过,我又想结果是不是等于字符串长度-最长回文子串长度,然后仔细一想,肯定不对的。但是问题到了这里,想明白了,其实应该等于字符串长度-最长回文子序列长度。方法就是以原来最长回文子序列中心字符为中心,不在原来回文字序列中的字符,对称插入即可。
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n=s.size();
int dp[n][n];
memset (dp,0,sizeof(dp));
for(int i=0;i<n;i++)
dp[i][i]=1;
for(int i=n-1;i>=0;--i)
{
for(int j=i+1;j<n;++j)
if(s[i]==s[j])
dp[i][j]=dp[i+1][j-1]+2;
else
dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
}
return dp[0][n-1];
}
int minInsertions(string s) {
return s.size()-longestPalindromeSubseq(s);
}
};
想明白上面之后,我在想要是不用最长回文子序列该如何呢?之前做的戳气球其实可以有点可以借鉴的。我们假设要求(i,j)区间,那么我们可以这样想(i+1,j)和(i,j-1)和(i+1,j-1)区间已知。如果s[i]和s[j]相等,那么dp[i][j]=dp[i+1][j+1],如果不相等,则选取dp[i][j-1]和dp[i+1][j]之间的最小值加一即可。因为s[i]和s[j]不等了,所以我们需要任意增加其中一个就好,但是这样就转化为(i+1,j)和(i,j-1)区间的问题了。
手写的时候,肯定是斜着写更符合,但是写代码太难了,只能从倒着写,
class Solution {
public:
int minInsertions(string s) {
int n=s.size();
if(n<2) return 0;
int dp[n][n];
memset(dp,0,sizeof(dp));
for(int i=n-2;i>=0;--i)
for(int j=i+1;j<n;++j)
if(s[i]==s[j])
dp[i][j]=dp[i+1][j-1];
else
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
return dp[0][n-1];
}
};