大意:求一个字符串在通过在任意位置增加一个字符使得变为一个回文串的最小操作数及打印出该回文串。
1 #include <map> 2 #include <stack> 3 #include <queue> 4 #include <math.h> 5 #include <stdio.h> 6 #include <string.h> 7 #include <iostream> 8 #include <algorithm> 9 using namespace std; 10 11 int dp[1010][1010]; 12 13 int Max(int x, int y) 14 { 15 if(x >= y) 16 return x; 17 return y; 18 } 19 20 void run() 21 { 22 char s[1010]; 23 int i, j; 24 while(gets(s)) 25 { 26 memset(dp, 0, sizeof(dp)); 27 int len = strlen(s); 28 for(i = 1; i <= len; i++) //记录路径,找到重复的字母 29 { 30 for(j = 1; j <= len; j++) 31 { 32 if(s[i-1] == s[len-j]) 33 dp[i][j] = dp[i-1][j-1]+1; 34 else 35 dp[i][j] = Max(dp[i-1][j], dp[i][j-1]); 36 } 37 } 38 printf("%d ", len-dp[len][len]); //打印次数 39 i = j = len; 40 while(i != 0 || j != 0) 41 { 42 if(s[i-1] == s[len-j] && i > 0 && j > 0) 43 { 44 printf("%c", s[i-1]); 45 i--,j--; 46 } 47 else if(dp[i][j] == dp[i][j-1] && j > 0) 48 { 49 printf("%c", s[len-j]); 50 j--; 51 } 52 else if(dp[i][j] == dp[i-1][j] && i > 0) 53 { 54 printf("%c", s[i-1]); 55 i--; 56 } 57 } 58 printf("\n"); 59 } 60 } 61 62 int main(void) 63 { 64 run(); 65 66 return 0; 67 }
找了另一种DP的方法:
这里需要用到一个结论,即增加字符变为回文串与减少字符变为回文串的最小操作数是相同的,这里可以用手模拟几种情况,就可以得出这个结论。
那么状态转移方程即为:
d[i][j] = d[i+1][j-1]; str[i] == str[j]
d[i][j] = min(d[i+1][j-1], d[i][j-1])+1; str[i] != str[j];
如何去打印路径呢?我们知道LCS中是通过记录当前操作,然后通过递归的方式来打印路径的,这里也类似。
由于回文串的特点是两边对称,所以,我们在把输入的字符串变为回文串的过程中可以通过左边的串来输出右边的串。
这样递归的特点即是: printf("%c", str[i]); print_ans(i+1,j -1); printf("%c", str[i]);
这样,即可保证字符串是左右对称的。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstring> 6 #include <string> 7 #include <queue> 8 using namespace std; 9 10 const int MAXN = 1010; 11 const int INF = 0x3f3f3f3f; 12 13 int d[MAXN][MAXN]; 14 int path[MAXN][MAXN]; 15 bool vis[MAXN][MAXN]; 16 17 char str[MAXN]; 18 19 void init() 20 { 21 memset(path, -1, sizeof(path)); 22 memset(vis, 0, sizeof(vis)); 23 } 24 25 int dp(int i, int j) 26 { 27 int &ans = d[i][j]; 28 if(i >= j) return 0; 29 if(vis[i][j]) return ans; 30 vis[i][j] = 1; 31 if(str[i] == str[j]) { ans = dp(i+1, j-1); path[i][j] = 0;} 32 else if(dp(i+1, j) < dp(i, j-1)) {ans = dp(i+1, j)+1; path[i][j] = 1;} // <=与 <的结果不同 33 else { ans = dp(i,j-1)+1; path[i][j] = 2;} 34 return ans; 35 } 36 37 void print_ans(int i, int j) 38 { 39 if(i > j) return ; 40 if(i == j) printf("%c", str[i]); 41 if(path[i][j] == 0) 42 { 43 printf("%c", str[i]); 44 print_ans(i+1, j-1); 45 printf("%c", str[i]); 46 } 47 else if(path[i][j] == 1) 48 { 49 printf("%c", str[i]); 50 print_ans(i+1, j); 51 printf("%c", str[i]); 52 } 53 else if(path[i][j] == 2) 54 { 55 printf("%c", str[j]); 56 print_ans(i, j-1); 57 printf("%c", str[j]); 58 } 59 } 60 61 void solve() 62 { 63 init(); 64 int n = strlen(str); 65 int ans = dp(0, n-1); 66 printf("%d ", ans); 67 print_ans(0, n-1); 68 printf("\n"); 69 } 70 71 int main() 72 { 73 while(~scanf("%s", str)) 74 { 75 solve(); 76 } 77 return 0; 78 }