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 }