题目大意:
给出一个长度不超过100的字符串(仅含小写字母),问最少加几个字符能让它变为一个回文串。
如:
abcd 可以最少需要加入三个字符变成回文,比如变成 dcbabcd.
aaaa 本身即是回文,则不需要加入字符,加入字符数最小为0.
aab 最少加入一个字符,比如变为 baab.
最多一百组测试数据,每组测试数据字符串长度不超过100.
一开始想了一个贪心的思路,结果WA,转头就想到了反例,发现贪心不行。
转向DP思路,我们用dp[i][j]表示 str[i] - str[j] 这个子串需要变成回文的最少的添加的字符。
嗯。。。。好像明了了一些了,然后思考转移的方程好像没有头绪呀 = =。
仔细发现每当我们扫到一个字符的时候,有这么几种情况:
1. 我们单独考虑字符 str[j] , 可能 str[i] - str[j - 1] 是一个回文串,我们将 str[j] 对称到 str[i] 的前面 , 解就是 dp[i][j - 1] + 1.
2. 我们单独考虑字符 str[i] , 可能 str[i + 1] - str[j] 是一个回文串 , 我们将 str[i] 对称到 str[j] 的后面 ,解就是 dp[i + 1][j] + 1.
3. 我们考虑字符 str[i] str[j] , str[i] == str[j] 情况下,只要内部回文就能回文,所以解就是 dp[i + 1][j - 1].
4. 我们考虑字符 str[i] str[j] , str[i] != str[j] 情况下,转移情况是 1 2 情况中的一种 , 或者还有一种情况,内部已经回文,那么我们就是首尾两个字符要回文,解就是 dp[i + 1][j - 1] + 2.
至此,转移情况理清了大致如下:
接下来是代码环节,从转移方程看出 i 依赖于 i + 1 情况,而 j 依赖于 j - 1情况,所以应该外层为 j 从 0 ~ n - 1 , 内层为 i 从 0 ~ j , 写出代码 。
PS:这题也是黑书上的一道DP的思考题,来源是IOI 2000年的比赛 。 想到那些初中的大神就有这样的思维, Orz.......
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 #define MAXN 105 7 int dp[MAXN + 5][MAXN + 5]; 8 char str[MAXN]; 9 10 int main(){ 11 int T; 12 while(~scanf("%d",&T)) for(int tt = 1 ; tt <= T ; tt++){ 13 scanf("%s",str); 14 int size = strlen(str); 15 for(int j = 0 ; j < size ; j++){ 16 for(int i = j ; i >= 0 ; i--){ 17 if(str[i] == str[j]){ 18 dp[i][j] = (i + 1 <= j - 1) ? dp[i+1][j-1] : 0; 19 } 20 else{ 21 dp[i][j] = min(dp[i+1][j]+1,dp[i][j-1]+1); 22 if(i + 1 <= j - 1) dp[i][j] = min(dp[i][j] , dp[i + 1][j - 1] + 2); 23 } 24 } 25 } 26 printf("Case %d: %d ", tt , dp[0][size - 1]); 27 } 28 return 0; 29 }