Vijos P3577 回文子序列 dp+方案输出

来源:互联网 发布:lr下载mac 破解版 编辑:程序博客网 时间:2024/05/29 18:39

【问题描述】

给定一个由小写字母组成的字符串,删除其中的0个或多个字符,使得剩下的字母(顺序不变)组成一个尽量长的回文串。如果有多解,输出字典序最小的。

【输入格式】

包含多组数据。每组数据仅一行,为一个长度不超过1000的非空字符串,字符串仅包含小写字母。

【输出格式】

对于每组数据,输出所求的最长的回文串。

【输入样例】

aabbaabb
computer
abzla
samhita

【输出样例】

aabbaa
c
aba
aha

【样例解释】

时间限制:1秒 内存限制:128M

————————————————————————————————————

分析:

dp实际上很好写,我们看成一个区间形成一个回文串,那么就有如下方程:
f(i,j)表示将i~j的字符串形成一个回文串的最长长度
f(i,j)=max( f(i+1,j),f(i,j-1),f(i+1,j-1)+(s[i]==s[j] ? 2 : 0) );
f(i,i)=1;

实际上就是从一个回文串的中间开始生成,不难理解(顺道一提实际上当s[i]==s[j]的时候f(i,j)一定是从f(i+1,j-1)转移过去的,完全可以证明,不是很想说呜呜呜。。。。就是区间[i,j]两端的两个字母用不用以及可能产生的回文串长的可能性的探讨,类似于最长公共子串方程同一性质的证明)。

重点是怎么输出方案,一开始每个状态给了个vector然后超级暴力直接一个个复制字符直接成了一个O(N^3)的算法了QAQ,直接爆炸ORZ

后来换了个方法,转移状态的时候可以不直接转移字符串,而是记录当前区间[i,j]是从哪个区间转移过来的,然后就基本上都可以在记忆化里面实现了(虽然我写的不是记忆化),遇到当前区间[i,j]使得s[i]==s[j]就直接把s[i]放入最后的路径里面然后去生成区间[i+1,j-1]的回文串。具体的实现可以参考代码。

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>#include<ctime>using namespace std;const int maxn=1005;char s[maxn];int f[maxn][maxn];int path[maxn],cnt;struct data{ int l,r; }ans[maxn][maxn];void dp(){    memset(f,0,sizeof(f));    int n=strlen(s+1);    for(int i=1;i<=n;i++) f[i][i]=1;    for(int len=2;len<=n;len++)    for(int i=1;i<=n-len+1;i++)    {        int j=i+len-1;        f[i][j]=max(f[i+1][j],f[i][j-1]);        f[i][j]=max(f[i][j],f[i+1][j-1]+(s[i]==s[j] ? 2 : 0));    }}bool cmp_path(int a,int b,int x,int y){    int aa,bb,xx,yy;    bool re=0,dec=0;    while(!dec)    {        while(f[a][b]!=0 && s[a]!=s[b])        {            aa=a,bb=b;            a=ans[aa][bb].l;            b=ans[aa][bb].r;        }           while(f[x][y]!=0 && s[x]!=s[y])        {            xx=x,yy=y;            x=ans[xx][yy].l;            y=ans[xx][yy].r;        }        if(s[a]!=s[x]) re=s[a]<s[x],dec=1;        if(f[a][b]==0 || f[x][y]==0) dec=1;        a++,b--,x++,y--;    }    return re;}void find_path(int i,int j){    if(i>=j) return;    if(ans[i][j].l!=0 || ans[i][j].r!=0) return;    if(s[i]==s[j])    {        find_path(i+1,j-1);        ans[i][j]=(data){i+1,j-1};    }    else if(f[i+1][j]>f[i][j-1])    {        find_path(i+1,j);        ans[i][j]=(data){i+1,j};    }     else if(f[i+1][j]<f[i][j-1])    {        find_path(i,j-1);        ans[i][j]=(data){i,j-1};    }    else    {        find_path(i+1,j);        find_path(i,j-1);        if(cmp_path(i+1,j,i,j-1))            ans[i][j]=(data){i+1,j};        else            ans[i][j]=(data){i,j-1};    }}void out_ans(){    memset(path,0,sizeof(path));    cnt=0;    int x=1,y=strlen(s+1),xx,yy;    while(f[x][y]!=0)    {        if(s[x]==s[y]) path[++cnt]=s[x];        xx=x,yy=y;        x=ans[xx][yy].l,y=ans[xx][yy].r;    }    for(int i=1;i<=cnt;i++)        printf("%c",path[i]);    for(int i=cnt-(f[1][strlen(s+1)]&1);i>=1;i--)        printf("%c",path[i]);    printf("\n");}int main(){    freopen("test.in","r",stdin);    freopen("test.out","w",stdout);    while(scanf("%s",s+1)==1)    {        dp();        memset(ans,0,sizeof(ans));        find_path(1,strlen(s+1));        out_ans();    }    return 0;}