POJ 1159 Palindrome (动态规划)

来源:互联网 发布:php 获取数组相同元素 编辑:程序博客网 时间:2024/06/06 03:42
Palindrome
Time Limit: 3000MS Memory Limit: 65536KTotal Submissions: 48269 Accepted: 16570

Description

A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a program which, given a string, determines the minimal number of characters to be inserted into the string in order to obtain a palindrome.

As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome.

Input

Your program is to read from standard input. The first line contains one integer: the length of the input string N, 3 <= N <= 5000. The second line contains one string with length N. The string is formed from uppercase letters from 'A' to 'Z', lowercase letters from 'a' to 'z' and digits from '0' to '9'. Uppercase and lowercase letters are to be considered distinct.

Output

Your program is to write to standard output. The first line contains one integer, which is the desired minimal number.

Sample Input

5Ab3bd

Sample Output

2

Source

IOI 2000

题意:
给一串字符,问最少加多少个字符可以使它变成回文字符串。
(回文字符串=从左往右看 和 从右往左看 串相同)

分析:

假设dp[i][j]表示字符串a[i..j]变成palindrome

至少需要花dp[i][j]个操作(i<=j)

那么

如果a[i]=a[j],那么dp[i][j]=dp[i+1][j-1]

否则,就是dp[i][j] = min(dp[i-1][j], dp[i][j-1]) +1  这是因为要在前面或者后面补一个数

 

初始化dp[i][i] = 0, dp[i][i+1]= a[i]==a[i+1] ? 0: 1

 

两种写法:

递归:

int getdp(int i,int j){

   if(i == j) return 0;

   if(i == j - 1) return a[i]==a[i+1] ? 0 : 2

   if(dp[i][j] != -1) return dp[i][j]

   if(a[i]==a[j]) return dp[i][j]=getdp(i + 1,j-1)

   else return dp[i][j]=min(getdp(i, j-1),getdp(i+1, j))+1

}

 

递推(i为间隔,间隔小的开始递归)

for(int i = 1;i<= n;i++){

  for(int j = 0;j < n - i;j++){

     if(a[j]==a[i+j]) dp[j][i+j] =dp[j+1][i+j-1]

     else dp[j][i+j]=min(dp[j+1][i+j],dp[j][i+j-1])+1

  }

}

 

注意dp[i][j]的范围i<=j


感想:

1、如果这个题换一种思路还可以用

      假设串s和它的逆串s'的最长公共子序列长度为L.

      需要添加的字符个数    串长s-L。

2、除了用short避免MLE,还可以用dp中常出现的滚动数组或者动态滚动数组

      来节省内存。

3、不过递归容易爆栈,斟酌着用。

4、终于搞清楚为什么有时候写的程序用c++提交AC,而用g++提交就wa。

      要从计算机原理说起


      内存实际上连续的一片,申请内存的时候,一些语言会强制检查越界,

      比如java。c++这个实际上数据就是一个指针,比如dp[0]就是*dp,

      dp[1]就是*(dp+1)。如果dp-1这块内存恰好没被使用,那么dp[-1]就不会

      报错了。


      如果一个二级数组dp的定义是dp[2][4]的话,那么
      dp[1][-1]实际上就是dp[0][3],一些编译器会报错,一些不会。


      在内存中,二级数据是这么存的:

      dp[0][0],dp[0][1],……,dp[0][M-1],dp[1][0],dp[1][1],……

      在编译器中,实际上会数组访问这么定位

      dp[i][j] = *(dp+i*M+j)

5、最有趣的是我开始把dp的初始条件弄错了,弄成

      dp[i][i+1]= (a[i]==a[i+1]?0:2)了,结果用递推写AC了,用递归

      却是wa.要不是因为递归wa了,我还不会发现我这里错了。。

       因为要加入的字符最少,所以尽管

       ab可以加两个字符使之变成abba、bab和aba.但是应该至少是加一个字符。


递推AC代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;short dp[5005][5005];char a[5010];int main(){    int n,i,j;    while(scanf("%d",&n)!=EOF)    {        getchar();        gets(a);        for(i=0;i<n;i++)        {            dp[i][i]=0;            dp[i][i+1]=(a[i]==a[i+1]?0:1);        }        for(i=1;i<=n;i++)        {            for(j=0;j<n-i;j++)            {                if(a[j]==a[j+i])                    dp[j][j+i]=dp[j+1][j+i-1];                else                    dp[j][j+i]=min(dp[j][j+i-1],dp[j+1][j+i])+1;            }        }        printf("%d\n",dp[0][n-1]);    }    return 0;}//AC//1407MS

递归AC代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;char a[5005];short dp[5005][5005];short getdp(int i,int j){    if(i==j) return 0;    if(i+1==j) return a[i]==a[j]?0:1;    if(dp[i][j]!=-1) return dp[i][j];    if(a[i]==a[j]) return dp[i][j]=getdp(i+1,j-1);    else return dp[i][j]=min(getdp(i,j-1),getdp(i+1,j))+1;}int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        getchar();        gets(a);        memset(dp,-1,sizeof(dp));        printf("%d\n",getdp(0,n-1));    }    return 0;}//AC//1438MS


      

      

      


原创粉丝点击