最长公共子序列:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列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;
很容易证明,如果能形成公共子序列,则最长子序列一定是从某个位置开始的对角线的长度,所以我们需要做的就是统计对角线的长度,DP方程就为if
a[i,j]=1 then d[i,j]:=d[i-1,j-1]+1;
经过循环我们就可以把上表转化成:
因此我们只需记录在循环中的最大值就是我们要的最长公共子序列的长度;
代码:
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--)