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;}
- Vijos P3577 回文子序列 dp+方案输出
- 输出最长回文子序列字符数
- 【DP】 最长公共回文子序列
- hihoCoder1149 求回文子序列数 dp
- 最长回文子序列 区间dp
- hdu 1513 Palindrome(dp 回文子序列)
- 【水DP 回文子序列】POJ 3280
- 求回文子序列个数(DP)
- 回文字符串【最长公共子序列】【DP】
- Vijos P1303 导弹拦截【最长上升子序列+DP】
- UVA 11151 Longest Palindrome(最长回文子序列 + dp + LCS)
- UVa 11404 - Palindromic Subsequence (最长回文子序列 DP)
- hdu4745 最长回文子序列(区间DP)
- 2015编程之美资格赛:回文子序列数 DP
- nefu1037回文子序列数(区间dp)
- hdu 4745 Two Rabbits(dp最长回文子序列)
- 最长回文子序列
- 最长回文子序列
- 适配器模式
- Java中列表框的使用
- 百度地图API学习笔记(一) 实现实时定位
- Linux学习的第一步
- HashTable和HashMap的区别详解
- Vijos P3577 回文子序列 dp+方案输出
- HDU-1394-Minimum Inversion Number-(树状数组)
- hdu2643(第二类斯特林数) Rank
- 剑指offer_数据流中的中位数
- js实现tab切换
- 2017年8月22日 星期二
- 再谈AbstractQueuedSynchronizer1:独占模式
- 洛谷P3386【模板】二分图匹配
- lvds在FPGA中的使用5