题目要计算一个字符串最少添加几个字符使其成为回文串。
一年多前,我LCS这道经典DP例题看得还一知半解时遇到一样的问题,http://acm.fafu.edu.cn/problem.php?id=1007。
当时完全靠自己瞎YY出了LCS的解法:
我当时这么想的:
- 把字符串分成两个部分,假设这两个部分分别就属于新的回文串的对称的两部分,那么要添加最少的字符串形成回文串就需要前一部分和后一部分公共部分最多,也就是它们的(一正一反)LCS。
- 这样就是枚举中间部分的位置(另外还要分情况讨论奇数和偶数回文串,奇数的枚举的就是中间元素的位置),然后计算两个部分的长度-2*LCS(一正一反),取最小的。
- 两个部分的长度的(一正一反)LCS可以通过计算原字符串和其反转串的LCS预处理出来,就是dp[i][j]的意义——s11...s1i和s21...s2j的LCS。
我搜了下,真有LCS的解法,不过不一样的是很简单就是长度-LCS就是解。真难想象当时能完全靠自己想出这个解法的我。。
现在做这题,只会用区间DP乱搞了,虽然状态转移只是靠着感觉去写,正确性一知半解。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define INF (1<<29) 6 int d[222][222]; 7 char str[222]; 8 int main(){ 9 int t; 10 scanf("%d",&t); 11 for(int cse=1; cse<=t; ++cse){ 12 scanf("%s",str); 13 int n=strlen(str); 14 for(int i=0; i<n; ++i){ 15 for(int j=i+1; j<n; ++j) d[i][j]=INF; 16 for(int j=0; j<=i; ++j) d[i][i]=0; 17 } 18 for(int len=2; len<=n; ++len){ 19 for(int i=0; i+len<=n; ++i){ 20 if(str[i]==str[i+len-1]) d[i][i+len-1]=d[i+1][i+len-2]; 21 else{ 22 for(int j=i; j<i+len-1; ++j) d[i][i+len-1]=min(d[i][i+len-1],d[i][j]+i+len-1-j); 23 for(int j=i+len-1; j>i; --j) d[i][i+len-1]=min(d[i][i+len-1],d[j][i+len-1]+j-i); 24 } 25 } 26 } 27 printf("Case %d: %d\n",cse,d[0][n-1]); 28 } 29 return 0; 30 }