题目大意:给你一串字符串,问最少添加几个字符可以使该字符串变成回文串。
解题思路:先求出该字符串的最长回文子串。然后总长度减去最长回文子串的长度就行了。已经形成回文的我们没有必要多此一举去添加字符了,而还单着的,我们在相对的位置在给他加一个就行了。
但是直接区间dp求最长回文字串,开二维数组会MLE,而又不太好滚动(不知道可不可以滚动),不过可以直接开short 莽过。
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int maxn=5005;
int n;
short dp[2][maxn];
string s;
int main(){
cin>>n>>s;
s.insert(s.begin(),' ');
for(int i=1;i<=n;++i) dp[i][i]=1;
for(int len=2;len<=n;++len){
for(int i=1;i+len-1<=n;++i){
int j=i+len-1;
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]);
}
}
cout<<n-dp[1][n]<<endl;
return 0;
}
这样投机取巧肯定是不好的。我们还可以将原字符串颠倒,然后求两个字符串的最长公共子串,这个dp改成二维滚动还是很好改的,画图看一下它的更新方式就知道了。
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int maxn=5005;
int n;
int dp[2][maxn];
char s1[maxn],s2[maxn];
int main() {
scanf("%d",&n);
getchar();
for(int i=1; i<=n; ++i) {
scanf("%c",s1+i);
s2[n+1-i]=s1[i];
}
for(int i=1; i<=n; ++i) {
for(int j=1; j<=n; ++j) {
if(s1[i]==s2[j]) dp[i%2][j]=dp[(i-1)%2][j-1]+1;
else dp[i%2][j]=max(dp[(i-1)%2][j],dp[i%2][j-1]);
}
}
cout<<n-dp[n%2][n]<<endl;
return 0;
}