zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    Quicksum -SilverN
    uva 140 bandwidth (好题) ——yhx
    uva 129 krypton factors ——yhx
    uva 524 prime ring problem——yhx
    uva 10976 fractions again(水题)——yhx
    uva 11059 maximum product(水题)——yhx
    uva 725 division(水题)——yhx
    uva 11853 paintball(好题)——yhx
    uva 1599 ideal path(好题)——yhx
    uva 1572 self-assembly ——yhx
  • 原文地址:https://www.cnblogs.com/HpuAcmer/p/2481323.html
Copyright © 2011-2022 走看看