首页 > 代码库 > nyist oj 37 回文字符串 (动态规划经典)

nyist oj 37 回文字符串 (动态规划经典)

回文字符串

时间限制:3000 ms  |  内存限制:65535 KB
难度:4
描述
所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba"。当然,我们给你的问题不会再简单到判断一个字符串是不是回文字符串。现在要求你,给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。
输入
第一行给出整数N(0<N<100)
接下来的N行,每行一个字符串,每个字符串长度不超过1000.
输出
每行输出所需添加的最少字符数
样例输入
1
Ab3bd
样例输出
2
来源

IOI 2000

开始看到这道题的时候,一时想不出用什么很好的方法来做,看到分类是在动态规划,也大致往这方面想,看了别人的思路,顿时茅塞顿开啊,直接把给定的字符串倒转,然后再和原字符串一起,求他们的最长公共序列,然后再拿字符串的长度减去他们的最长公共序列的长度,得到的就是要添加的最小的字符数,想到了这个地方,这个题目就很好解啦;直接用LIC水过,状态方程式也和前面做过的题一样;

#include <cstdio>
#include <cstring>
#define max(a,b) a>b?a:b
const int maxn=1001;
char a[maxn],b[maxn];
int dp[maxn][maxn];//昨天晚上把DP的类型设置成了char型,然后提交一直wa,刷屏了。。。不应该啊!!
int main()
{
    int n,i,j,len;
    scanf("%d",&n);
    while(n--)
    {
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        scanf("%s",a);
        len=strlen(a);
        for(i=len-1,j=0;i>=0;i--)
            b[j++]=a[i];
        for(i=1;i<=len;i++)
        {
            for(j=1;j<=len;j++)
            {
                if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;//递推关系
                else
                  dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
        printf("%d\n",len-dp[len][len]);
    }
}
看到别人的写的,用了另一种思路,也是动态规划,但是递推关系有一点不同;有点没看懂;

省去了倒转的环节

 
#include<stdio.h>  
#include<string.h>  
  
int f[1005][1005];  
  
int main()  
{  
    int n;  
    scanf("%d",&n);  
    while (n--)  
    {  
        char s[1005];  
        scanf("%s",s);  
        int k,i,j,l=strlen(s);  
  
        for (i=0;i<l;++i) f[i][i]=0;  
  
        for (k=2;k<=l;++k)  
            {  
                for (i=0;i<=l-k;++i)  
                    {  
                    int p=i+k-1;  
                    if (s[i]==s[p])  
                        {  
                            f[i][p]=f[i+1][p-1];  
                        }  
                    else   
                        {  
                            f[i][p]=1+(f[i][p-1]<f[i+1][p]?f[i][p-1]:f[i+1][p]);  
                        }  
                    }  
            }  
  
        printf("%d\n",f[0][l-1]);  
        memset(f,0,sizeof(f));  
    }  
  
      
    return 0;  
}          

看到别人的最优代码,内存占用的很小,值得学习,还有一种滚动数组,好像可以节约内存,还没有接触过;

 
 
#include<stdio.h>
#include<string.h>
using namespace std;
int m[1000],i,j,t1,t2,len;
char s[1001];
int main() {
  int N;
  scanf("%d",&N);
  while(N--){
    scanf("%s",s);
    len=strlen(s);
    for(i=len-1;i>=0;i--){
      m[i]=0;
      t1=m[i];
      for(j=i+1;j<len;j++){
        t2=m[j];
        if(s[i]==s[j])
          m[j]=t1;
        else
          m[j]=m[j-1]<m[j]?m[j-1]+1:m[j]+1;
        t1=t2;
      }
    }
    printf("%d\n",m[len-1]);
  }
}