String to Palindrome
题目大意:给出一个字符串s,现在可以进行3种操作(添加字母,删除字母,替换字母),将其变成回文串,求出最少的操作次数。比如abccda,可以用删除操作,删除b,d两步可变成回文;但如果用替换操作,把b换成d则只需要1步。
分析:刚开始我一直考虑它是否具有最优子结构性质,直到现在,还是不明白为什么可以用动态规划来做,大神若是看见,还望指教。
由于添加字母和删除字母的效果是一样的,因此我们这里就只进行删除和替换操作。令dp[i][j]表示从第 i 到第 j 个字母变成回文所需要最少的操作数。
转移方程为:当是s[i]==s[j]时,dp[i][j] = dp[i+1][j-1];此外dp[i][j] = min(dp[i+1][j],dp[i+1][j-1],dp[i][j-1]) + 1; dp[i+1][j-1]+1 是替换操作,其他两种是删除操作。
初始条件是:j<=i 时dp[i][j] = 0; 如果只是单一的字母,它本身就是回文
递推代码如下:
1 # include<cstdio> 2 # include<cstring> 3 # include<iostream> 4 using namespace std; 5 char s[1005]; 6 int dp[1005][1005]; 7 int main() 8 { 9 int T,cas; 10 scanf("%d",&T); 11 for(cas=1; cas<=T; cas++) 12 { 13 scanf("%s",s); 14 int len =strlen(s); 15 int i,j; 16 for(i=0; i<len; i++) 17 dp[i][i] = 0; 18 for(i=len-1; i>=0; i--) 19 for(j=i+1; j<len; j++) 20 { 21 if(s[i]==s[j]) 22 dp[i][j] = dp[i+1][j-1]; 23 else 24 dp[i][j] = min(min(dp[i+1][j],dp[i+1][j-1]),dp[i][j-1])+1; 25 } 26 printf("Case %d: %d\n",cas,dp[0][len-1]); 27 } 28 return 0; 29 }
递归代码如下:
1 # include<cstdio> 2 # include<cstring> 3 # include<iostream> 4 using namespace std; 5 char s[1005]; 6 int dp[1005][1005]; 7 int DP(int x,int y){ 8 if(dp[x][y] != -1) 9 return dp[x][y]; 10 if(y <= x ) 11 return dp[x][y] = 0; 12 if(s[x] == s[y]) 13 dp[x][y] = DP(x+1,y-1); 14 else 15 dp[x][y] = min( min(DP(x+1,y),DP(x+1,y-1)),(DP(x,y-1))) + 1; 16 return dp[x][y]; 17 } 18 int main(){ 19 int T,cas; 20 scanf("%d",&T); 21 for(cas=1;cas<=T;cas++){ 22 scanf("%s",s); 23 int len =strlen(s); 24 memset(dp,-1,sizeof(dp)); 25 printf("Case %d: %d\n",cas,DP(0,len-1)); 26 } 27 return 0; 28 }