题意:给出一个长度不超过1000的由小写字母组成的字符串,使用增、删、改使其变成回文串,问最少操作数。
题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=114&problem=1680
——>>设d[i][j]为使第i位到第j位变成回文串的最少操作数,
当s[i] == s[j]时,
直接去掉两端,d[i][j] = dp(i+1, j-1);
当s[i] != s[j]时,
dp(i+1, j) + 1,dp(i, j-1) + 1同时表示增删,dp(i+1, j-1) + 1表示改;
故此时 d[i][j] = min(dp(i+1, j), dp(i, j-1), dp(i+1, j-1)) + 1;
边界条件:当i == j时,当然是0;
当j == i+1时,若s[i] == s[j],已是回文串,故为0;否则或增或删或改都只需1步,故为1;
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1000 + 10, INF = (1<<30); int d[maxn][maxn]; char s[maxn]; int min(int a, int b, int c) //求三数最小数 { a = (a < b) ? a : b; a = (a < c) ? a : c; return a; } int dp(int i, int j) { int& ans = d[i][j]; if(ans != INF) return ans; if(j == i+1) { if(s[i] == s[j]) return ans = 0; else return ans = 1; } if(s[i] == s[j]) //两端相等 ans = dp(i+1, j-1); else //两端不等可增、删、改 ans = min(dp(i, j-1), dp(i+1, j), dp(i+1, j-1)) + 1; return ans; } int main() { int T, i, j, cnt = 1; scanf("%d", &T); while(T--) { getchar(); cin>>(s+1); int len = strlen(s+1); for(i = 0; i < maxn; i++) { for(j = 0; j < maxn; j++) d[i][j] = INF; d[i][i] = 0; } dp(1, len); printf("Case %d: %d\n", cnt++, d[1][len]); } return 0; }