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;
    }
    
    
  • 相关阅读:
    函数的有用信息,装饰器 day12
    函数名、闭包、装饰器 day11
    函数的动态参数与命名空间 day10
    函数 day9
    集合 day8
    文件操作 day8
    基础数据类型补充,及capy daty7
    day7 回顾
    编码补充 daty 6
    字典的增删改查 daty 5
  • 原文地址:https://www.cnblogs.com/HpuAcmer/p/2481323.html
Copyright © 2011-2022 走看看