hdu1159java_hdu1159

最长公共子序列:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X="x0,x1,...,xm

-1",序列Y="y0,y1,...,yk-1"是X的子序列,存在X的一个严格递增下标序列,使得对所有的j=0,1,...,k-1,有xij=

yj。例如,X="ABCBDAB",Y="BCDB"是X的一个子序列。给定两个序列A和B,称序列Z是A和B的公共子序列,是指Z同是A和B的子序列。问题要求已知两序列A和B的最长公共子序列。

本题初看有些类似线形动态规划,但经过仔细审题发现用矩阵类动态规划更好理解,如下图所显示,我们把两个字符串看作矩阵的横纵轴:首先读入数据时,把两个字符相同的标记成1,不相同的地方标记成0;

a4c26d1e5885305701be709a3d33442f.png

很容易证明,如果能形成公共子序列,则最长子序列一定是从某个位置开始的对角线的长度,所以我们需要做的就是统计对角线的长度,DP方程就为if

a[i,j]=1 then d[i,j]:=d[i-1,j-1]+1;

经过循环我们就可以把上表转化成: a4c26d1e5885305701be709a3d33442f.png

因此我们只需记录在循环中的最大值就是我们要的最长公共子序列的长度;

代码:

len1=s1.length();

len2=s2.length();

for(j=0;j<=len1;j++)

{

len[0][j]=0;

}

for(i=0;i<=len2;i++)

{

len[i][0]=0;

}

for(i=1;i<=len1;i++)

{

for(j=1;j<=len2;j++)

{

if(s1[i-1]==s2[j-1])

{

len[i][j]=len[i-1][j-1]+1;

flag[i][j]=1;

}

else

{

len3=len[i-1][j];

len4=len[i][j-1];

if(len3>=len4)

{

len[i][j]=len3;

flag[i][j]=2;

}

else

{ flag[i][j]=3;

len[i][j]=len4;

} }

} }

优化:但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。

当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。

我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,

最长匹配子串的位置和长度就已经出来了.这样做速度比较快,但是花的空间太多。我们注意到在改进的矩阵生成方式当中,每生成当前一层,前面的那一层就已经没有用了。

因此我们只需使用一维数组即可。可以自己写一下。

变种之一:字串距离

下面是具体题目;

hdu1159

问题描述我们称序列Z=

...,zk>是序列X=的子序列当且仅当存在严格上升的序列,使得对j=1,2,...,k,

有xij=zj。比如Z=

是X=的子序列。

现在给出两个序列X和Y,你的任务是找到X和Y的最大公共子序列,也就是说要找到一个最长的序列

Z,使得Z既是X的子序列也是Y的子序列。

输入

输入包括多组测试数据。每组数据包括一行,给出两个长度不超过200的字符串,表示两个序列。两个字符串之间由若干个空格隔开。

输出

对每组输入数据,输出一行,给出两个序列的最大公共子序列的长度。

输入样例

abcfbc abfcab

programming contest

abcd mnp输出样例

4

2

0

代码:

#include

#include

using namespace std;

#define MAX 200

string a,b;

int len[MAX][MAX];

int flag[MAX][MAX];

int num;

void check(string s1,string s2)

{

int

len1,len2,len3,len4;

int i,j,t;

char z[MAX];

len1=s1.length();

len2=s2.length();

for(j=0;j<=len1;j++)

{

len[0][j]=0;

}

for(i=0;i<=len2;i++)

{

len[i][0]=0;

}

for(i=1;i<=len1;i++)

{

for(j=1;j<=len2;j++)

{

if(s1[i-1]==s2[j-1])

{

len[i][j]=len[i-1][j-1]+1;

flag[i][j]=1;

}

else

{

len3=len[i-1][j];

len4=len[i][j-1];

if(len3>=len4)

{

len[i][j]=len3;

flag[i][j]=2;

}

else

{ flag[i][j]=3;

len[i][j]=len4;

} }

} }

cout<

i=len1;

j=len2;

t=0;

while(1)

{

if(i==0||j==0) {

break;

}

if(flag[i][j]==1)

{

z[t++]=s1[i-1]; i=i-1;

j=j-1;

}

else

if(flag[i][j]==2) {

i=i-1;

}

else if(flag[i][j]==3)

{

j=j-1;

}

}

for(i=t-1;i>=0;i--)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值