POJ 1159 (DP)
题目:http://poj.org/problem?id=1159
思路:
找出原串的最长回文子串,当然这里说的回文子串可以不连续。用原串的长度减去最长回文子串的长度即可得出结果。
设原串a[5001],它的反串为b[5001],求出a和b的最长公共子串的长度(可以不连续),即为回文子串的长度。再用原串长度减去回文子串的长度即可。
用动态规划求公共子串的长度,m[5001][5001]打表。m[i][j]表示原串a第1个到第i个和反串b第1个到第j个的最长公共子串的长度。所以有两种情况:
(1)当a[i] = b[j]时,m[i][j]=m[i-1][j-1] + 1;
(2) a[i] != b[j]时,m[i][j]=max(m[i-1][j],m[i][j-1])。
所以m[len][len]就是最长公共子串的长度。(len为原串的长度)
算法正确性证明:
比如abcdb,最长回文串是bcb或bdb,长度是3,5-3=2,所以只需插入2个即可。为什么呢???
因为回文串有两种形式aba或者abba,我们暂且把后面那两个b看成是一个,这个没关系的,便于解释。那么一个字符串就分成了两类字符了,一个是回文串,一个是非回文串,假设把回文串的中间那个字符记成b,那么b左边的非回文串和b右边的非回文串就不可能有交集(如果有,那交集部分就会归并到回文串里),所以只需要在左边对称位置插入b右边的非回文字符,在右边插入b左边的非回文字符。所以要插入的字符个数就是非回文字符的个数,非回文字符就是回文字符串对原字符串的补集,故原串长度减去回文串长度即可得解。
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 5001 char a[N],b[N]; unsigned short m[N][N]; int main() { int len,i,j,t; while(scanf("%d",&len) != EOF ) { scanf("%s",a); t = len -1; for(i = len-1; i >= 0 ; --i) b[t-i] = a[i] ; for(i = 1 ; i <= len ; ++i) for(j = 0 ; j < i ; ++j) m[i][j] = m[j][i] = 0; for(i = 0 ; i <= len ; ++i) m[i][i] = 0; for(i = 1 ; i <= len ; ++i) { for(j = 1 ; j <= len ; ++j) { if(a[i-1] == b[j-1]) m[i][j] = m[i-1][j-1] + 1; else m[i][j] = m[i-1][j] > m[i][j-1] ? m[i-1][j]:m[i][j-1]; } } printf("%d\n",len - m[len][len]); } // system("pause"); return 0; }
开始用 int m[N][N]; 超内存了!用unsigned short 就AC了。
看discuss,人家用滚动数组!汗,落伍了。第一次听说这玩意,于是诚信学习了啊!
先贴个最简单的滚动数组的应用:(求Fabonacci数列的第100个数)
int d[3];
d[0]=1;d[1]=1;
for(i=2;i<100;i++)
d[i%3]=d[(i-1)%3]+d[(i-2)%3];
printf("%d",d[99%3]);
注意上面的运算,我们只留了最近的3个解,数组好象在“滚动‿一样,所以叫滚动数组。
好了,这个题就可以用滚动数组+DP AC了。用一个二位的数组m[2][N]。列可以往后展开,行不停的滚动,
滚动方式: i%2,(i-1)%2
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 5001 unsigned short m[2][N]; char a[N],b[N]; int main() { int len,i,j,t; while(scanf("%d",&len) != EOF ) { scanf("%s",a); t = len -1; for(i = len-1; i >= 0 ; --i) b[t-i] = a[i] ; for(i = 0 ; i <= len ; ++i) m[0][i] = m[1][i] = 0; for(i = 1 ; i <= len ; ++i) { for(j = 1 ; j <= len ; ++j) { if(a[i-1] == b[j-1]) m[i%2][j] = m[(i-1)%2][j-1] + 1; else m[i%2][j] = m[(i-1)%2][j] > m[i%2][j-1] ? m[(i-1)%2][j]:m[i%2][j-1]; } } printf("%d\n",len - m[len%2][len]); } //system("pause"); return 0; }