1007 - 最长公共子序列(输出该子序列及其长度)

有一天夜晚,我烧毁了所有的记忆,从此我的梦透明了;有一个早晨,我扔掉了所有的昨天,从此我的脚步就轻盈了。

                                                                                                                                                                                            --泰戈尔

今天是国庆的最后一天了,感觉时间过的好快,待在机房的时间总是那么短暂,从明天开始就要停课了

愿自己丢掉所有包袱,好好地冲刺今年的NOIP,不负韶华,不负青春

其实如果光论最长公共子序列的长度,线性dp,最最基础的,就可以了

但如果还要求我们多一项任务:输出最长公共子序列

就需要再想想咯

先看一道题吧:

最长公共子序列

描述

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=,则另一序列Z=是X的子序列是指存在一个严格递增的下标序列,使得对于所有j=1,2,…,k有:Xij=Zj

例如,序列z=是序列X=的子序列,相应的递增下标序列为<2,3,5,7>。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称z是序列x和Y的公共子序列。例如,若x=和Y=,则序列是X和Y的一个公共子序列,序列 也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列.因为x和Y没有长度大于4的公共子序列。

给定两个序列X=和Y=.要求找出X和Y的一个最长公共子序列。

输入

输入文件共有两行。每行为一个由大写字母构成的长度不超过200的字符串,表示序列X和Y

输出

输出文件第一行为一个非负整数。表示所求得的最长公共子序列的长度。若不存在公共子序列.则输出文件仅有一行输出一个整数0。否则在输出文件的第二行输出所求得的最长公共子序列(也用一个大写字母组成的字符串表示)。若符合条件的最长公共子序列不止一个,只需输出其中任意的一个。

样例输入

ABCBDAB 
BDCABA

样例输出

4
BCBA

 

分析

对于长度,我们用一个基础的线性dp来搞

定义 f[i][j] 表示处理了 a 字符串的1~ i 和 b 字符串的 1 ~ j ,最长的公共子序列长度是多少

转移方程:

f[i][j]=max\left \{ f[i-1][j],f[i][j-1]\right \}

如果当前的 a[i] == b[j] 则多一步  f[i][j]=max(f[i][j],f[i-1][j-1]+1)

这些应该都很好理解,最后的答案就存在:f [ strlen(a+1) ] [ strlen(b+1) ]中,表示处理完 a 字符串和 b 字符串后得到的最长长度

现在我们来讲一下如何输出最长的LCS(借鉴网上的一张表)

我们可以在转移的时候记录下,当前状态是由之前的哪一个状态转移而来,最后输出的时候就从后往前递归处理

 

代码

//虽然我很菜,但我也会很努力! 
#include
using namespace std;
char a[205],b[205];
int f[205][205],g[205][205];//g存储当前状态是由之前三种可能的哪一种转移而来 
void getans(int n,int m){
	if(!n||!m) return;
	if(g[n][m]==1){//只有这种情况才需要输出,结合我给的表就清楚了 
		getans(n-1,m-1);
		printf("%c",a[n]);
	}
	else if(g[n][m]==-1) getans(n-1,m);
	else getans(n,m-1);
}
int main(){
	scanf("%s%s",a+1,b+1);
	int lena=strlen(a+1),lenb=strlen(b+1);
	for(int i=1;i<=lena;++i){
		for(int j=1;j<=lenb;++j){
			if(f[i-1][j]>f[i][j]) f[i][j]=f[i-1][j],g[i][j]=-1;
			if(f[i][j-1]>f[i][j]) f[i][j]=f[i][j-1],g[i][j]=0;
			if(a[i]==b[j]){
				if(f[i-1][j-1]+1>f[i][j]){
					g[i][j]=1;
					f[i][j]=f[i-1][j-1]+1;
				}
			} 
		}
	}
	if(!f[lena][lenb]) printf("0");
	else{
		printf("%d\n",f[lena][lenb]);
		getans(lena,lenb);
	}
	return 0;
}

 

你可能感兴趣的:(普通DP)