问题描述:
序列Z=(B,C,D,B),序列X=(A,B,C,B,D,A,B),序列Z是序列X的子序列(subsequence)。因为Z中的元素在X中满足严格递增的下标(2,3,5,7),设第一个字符‘A’下标为1。
给定两个序列X,Y,如果序列Z即是X的子序列又是Y的子序列,则称Z是X,Y的公共子序列(common subsequence)。
(注意:如果求连续的序列,叫子串;不连续的序列,叫子序列。)
如果使用普通的字符串比较方法,穷举X的所有子序列,判断是否也为Y的子序列,如果是则记录下长度,最后通过比较得到最长的长度。
但是,这种方法太麻烦。例:一个含有m个元素的序列,它含有2^m个子序列。穷举法求解的时间复杂度在指数级别。
动态规划法:
最优子结构性质:
对序列X={x1,x2.....xm},Y={y1,y2......yn},Z={z1,z2,......zk}是X,Y的最长公共子序列。则满足一下关系式:
1.如果xm==yn,则zk==xm==yn,且X={x1,x2.....x(m-1)},Y={y1,y2......y(n-1)}的最长公共子序列为Z={z1,z2,......z(k-1)};
2.如果xm!=yn,且zk!=xm,则Z是X(m-1)和Y的最长公共子序列;
3.如果xm!=yn,且zk!=yn,则Z是Y(m-1)和X的最长公共子序列.
因为X,Y得最长公共子序列包含了两个序列前缀的最长公共子序列,表示最长公共子序列具有最优子结构特性。
最优解的递推关系:
1.如果xm==yn,则求出X(m-1)与Y(n-1)的最长公共子序列,然后+1,得到Xm与Yn的最长公共子序列;
2.如果xm!=yn,则分别求X(m-1)与Y,X与Y(n-1)的最长公共子序列,两个公共子序列中较长者就是X,Y的最长公共子序列。
用矩阵来表示这种关系较为方便。
矩阵满足一下递推式:
0 i=0,j=0
c[i][j]= c[i-1][j-1]+1 i,j>0&&xi=yj
max{c[i-1][j],c[i][j-1]} i,j>0&&xi!=yj
代码实现
//s1,s2为两个序列,a,b为两个二位数组,存储长度与关系
char s1[1000],s2[1000],s3[1000],s4[1000];int **a,int **b;
void init() //初始化数据
{
s1[0]='0'; //至于为什么要在序列s1前加一个‘0’字符,看下面的例子。
s2[0]='0'
cin>>s3;
cin>>s4;
strcat(s1,s3);
strcat(s2,s4);
int l1=strlen(s1);
int l2=strlen(s2);
a=new int*[l1];
b=new int*[l1];
for(int i=0;i<l1;i++)
{
a[i]=new int[l2];
for(int j=0;j<l2;j++)
{
a[i][0]=a[0][j]=0;
b[i][0]=b[0][j]=0;
}
}
计算最长公共序列:
int LCSLength(char *s1,char *s2,int **a,int **b,int l1,int l2)
{
for(int i=1;i<l1;i++)
{
for(int j=1;j<l2;j++)
{
if(s1[i]==s2[j])
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
else
if(c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else
{
c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
return a[l1-1][l2-1]; //最优解
}
如果想输出这个序列,调用一下函数
void CL(int i,int j)
{
if(i==0||j==0) return;
if(b[i][j]==1)
{
CL(i-1,j-1);
cout<<s1[i];
}
if(b[i][j]==2)
{
CL(i-1,j);
}
if(b[i][j]==3)
{
CL(i,j-1);
}
}
例子:
s1="0abcbdab" s2="0bdcaaba"
二维数组a,b
0 | 1(b) | 2(d) | 3(c) | 4(a) | 5(b) | 6(a) | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(a) | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
2(b) | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
3(c) | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
4(b) | 0 | 1 | 1 | 2 | 2 | 3 | 3 |
5(d) | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
6(a) | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
7(b) | 0 | 1 | 2 | 2 | 3 | 4 | 4 |
0 | 1(b) | 2(d) | 3(c) | 4(a) | 5(b) | 6(a) | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(a) | 0 | 2 | 2 | 2 | 1 | 3 | 1 |
2(b) | 0 | 1 | 3 | 3 | 2 | 1 | 3 |
3(c) | 0 | 2 | 2 | 1 | 3 | 2 | 2 |
4(b) | 0 | 1 | 2 | 2 | 2 | 1 | 3 |
5(d) | 0 | 2 | 1 | 2 | 2 | 2 | 2 |
6(a) | 0 | 2 | 2 | 2 | 1 | 2 | 1 |
7(b) | 0 | 1 | 2 | 2 | 2 | 1 | 2 |
可用测算法测试 POJ 1458 http://poj.org/problem?id=1458