POJ 1159-Palindrome(DP)

来源:互联网 发布:淘宝鄂尔多斯旗舰店 编辑:程序博客网 时间:2024/06/06 04:36

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

                                                                       

题意:求出字符串至少插入几个会变成回文串。

思路1:以i 作为字符串长度的控制, jj 作为左指针, j 作右指针,dp[jj][j] 表示 jj 到 j 需要插入几个才可成为回文串。

              dp数组定义为 int 时爆内存,使用short。

CODE:

#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <string>#include <cstring>#include <queue>#include <stack>#include <vector>#include <set>#include <map>const int inf=0xfffffff;typedef long long ll;using namespace std;char s[5005];short int dp[5005][5005];int main(){    //freopen("in", "r", stdin);    int n;    while(~scanf("%d", &n)){        //memset(dp,0,sizeof(dp));        scanf("%s", s+1);        for(int i=1; i<=n-1; i++){            for(int jj=1;jj+i<=n;jj++){                int j=jj+i;                if(s[jj]==s[j]){                    dp[jj][j]=dp[jj+1][j-1];                }                else{                    dp[jj][j]=min(dp[jj+1][j], dp[jj][j-1]) + 1;                }            }        }        cout<<dp[1][n]<<endl;    }    return 0;}

思路2:将题目转化为求解最长公共序列,s2 为 原字符串s1的逆序,

              ans = n-s1 和 s2 的最长公共子序列数。

             但是定义一个dp[5005][5005] 会爆内存,于是使用滚动数组。

             PS :滚动数组 : 滚动数组实际是一种节省空间的办法,时间上没啥优势,多用于DP中,举个例子吧: 

                       一个DP,平常如果需要1000×1000的空间,其实根据DP的无后效性,可以开成2×1000,然后通过滚动,获得和1000×1000一样的效果。滚动数组常用于DP之        中,在DP过程中,我们在由一个状态转向另一个状态时,很可能之前存储的某些状态信息就已经无用了,例如在01背包问题中,从理解角度讲我们应开DP[i][j]的二维数组,第一维我们存处理到第几个物品,也就是阶段了,第二维存储容量,但是我们获得DP[i],只需使用DP[i - 1]的信息,DP[i - k],k>1都成了无用空间,因此我们可以将数组开成一维就行,迭代更新数组中内容,滚动数组也是这个原理,目的也一样,不过这时候的问题常常是不可能缩成一维的了,比如一个DP[i][j]需要由DP[i - 1 ][k],DP[i - 2][k]决定,i<n,0<k<=10;n <= 100000000;显然缩不成一维,正常我们应该开一个DP[100000005][11]的数组,结果很明显,超内存,其实我们只要开DP[3][11]就够了DP[i%3][j]由DP[(i - 1)%3][k]和DP[(i - 2)%3][k]决定,空间复杂度差别巨大。

CODE:

#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <string>#include <cstring>#include <queue>#include <stack>#include <vector>#include <set>#include <map>const int inf=0xfffffff;typedef long long ll;using namespace std;char s1[5005], s2[5005];int dp[2][5005];int main(){    //freopen("in", "r", stdin);    int n;    while(~scanf("%d",&n)){        memset(dp,0,sizeof(dp));        getchar();        for(int i=1, j=n; i<=n; i++,j--){            char ch;            scanf("%c", &ch);            s1[i]=s2[j]=ch;        }        for(int i=1;i<=n;i++){            for(int j=1;j<=n;j++){                if(s1[i] == s2[j]){                    dp[i%2][j]=dp[(i-1)%2][j-1]+1;                }                else{                    dp[i%2][j]=max(dp[i%2][j-1] , dp[(i-1)%2][j]);                }            }        }        printf("%d\n",n-dp[n%2][n]);    }    return 0;}



0 0
原创粉丝点击