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;
}

 

 

 

 

 

 

posted @ 2012-05-03 21:13  开开甲  阅读(323)  评论(0编辑  收藏  举报