最长公共子序列(LCS)和最优二叉搜索树

来源:互联网 发布:淘宝秋冬裙子 编辑:程序博客网 时间:2024/06/03 11:00

最长公共子序列-------

比较两个串的相似度有这样一种方法:寻找第三个串S3,它的所有序列也都出现在S1 和S2中,且在三个串中出现的次序都相同,但不要求连续出现,可以找到的S3越长就可以认为S1S2相似度越大, 这就是**最长公共子序列**的问题

形式化定义如下:给定一个序列X= {x1,x2..xm},另一个序列Z= {z1,z2,..zk}满足如下条件时称为X的子序列:存在一个严格递增的X的下表序列<i1,i2..ik>对所有j = 1,2,3..k满足 xij = zj。

例如 Z= <B,C,D,B>是X= <A,B,C,B,D,A,B>的一个子序列给定两个序列X和Y,如果Z既是X的子序列也是Y的子序列,则为X和Y的公共子序列

如X= <A,B,C,B,D,A,B>,Y= <,A,C,F,>,Z= <B,C,D,B>是公共子序列最长公共子序列问题给定两个序列X,Y求X和Y长度最长的公共子序列(LCS问题)

S1:刻画最长公共子序列

LCS问题具有最优子结构性质,子问题的自然分类对应两个输入序列的”前缀”对;

前缀的严谨定义是:给定一个序列X= {x1,x2..xm},对i = 0,1,...,m定义X的第i前缀为Xi = <x1,x2...xi>

LCS的最优子结构:

令X = <x1,x2...xm>, Y = <y1,y2,..yn>为两个序列,Z = <z1,z2..zk>为X和Y的任意LCS

1、如果xm = yn, 则zk = xm = yn且 Zk-1是 Xm-1 和Yn-1的一个LCS

2、如果xm != yn,那么zk != xm意味着Z是Xm-1和Y的一个LCS

3、如果xm != yn,那么zk != yn意味着Z是X和Yn-1的一个LCS

S2:一个递归解

要求解一个或两个子问题如果xm = yn只要求Xm-1 和Yn-1的一个LCS,然后把xm加到最后如果不等,就要求两个子问题Xm-1和Y的一个LCS,X和Yn-1的一个LCS,找出比较长的一个先从递归的容易思考的角度去想定义c[i , j]表示Xi 和Yj的LCS的长度,如果i = 0 || j = 0,那么LCS长度肯定为0 ,可以得到:

0 i==0|| j==0

c[i , j] = c[i-1 , j-1]+1 i,j>0 && xi = yj

max(c[i, j-1], c[i-1 , j]) i,j>0&&xi!=yj


S3:计算LCS的长度

将递归思想用动态规划实现接受两个序列为输入,将c[i , j]的值保存在表 c[0..m, 0..n]中,并按行主次序计算,过程还维护一个表格b[1..m, 1..n]帮助构造最优解 “X”为 X序列退一步的,‘Y’为Y序列退一步的, ‘D’表示两者最后一个一样,递归找前面的伪代码如下:

```LCS(X,Y)

{ m = X.length; n = Y.length;

let b[1..m, 1..n] and c[0..m, 0..n]be new tables; //一方长度为0时,公共子串长为0

for(int i = 1;i<=m;i++) c[i,0] = 0;

for(int i = 1;i<=n;i++) c[0,i] = 0;

for(int i = 1;i<=m;i++)

for(int j = 1;j<=n;j++)

{if(xi == yj)//最后一个相等

{c[i , j] = c[i-1, j-1]+1; b[i,j] = 'D';}

else if(c[i-1, j]>= c[i, j-1])//X序列退一个与当前Y找的公共串长

{c[i , j] = c[i-1,j]; b[i,j] ='X';}

else {c[i , j] = c[i, j-1]; b[i,j] = 'Y';}

}

return c[], b[];

}```

S4:构造LCS

```Print_Lcs(b, X, i, j)

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

if(b[i,j]=='D') {Print_Lcs(b,X,i-1,j-1); print xi;}

else if(b[i,j]=='X')Print_Lcs(b,X,i-1,j);

else Print_Lcs(b,X,i,j-1);

}

```起始调用是 (b, X, X.length, Y.length)


最优二叉搜索树

-----------

给定一个n个不同关键字的已排序的序列K=<k1, k2,..kn>,用这些关键字构造一颗二叉搜索树。对每个关键字ki都有一个概率pi表示其搜索频率,有些要搜索的值可能不在k中,因此还有n+1个“伪关键字”d0, d1,d2..dn,d0 表示小于k1的值, dn表示大于kn的值,伪关键字di表示所有在关键字ki ki+1之间的值,每个伪关键字也有对应的qi。通常ki是内部的点,di是叶节点

对一颗搜索树搜索一次的期望为 E[T] = (depth(ki)+1)*pi(i = 1...n 的和)+ (depth(di)+1)*qi(i = 0...n求和) 

搜索代价最小的树就是最优二叉搜索树 

S1:最优二叉搜索树的结构

一棵最优二叉树包含的子树必然是作为实例的最优结构

S2:一个递归算法

子问题域为求解包含关键字ki..kj的最优二叉搜索树,i>=1&& j>=i-1&&j<=n(j = i-1时子树不包含实际关键字,只包含伪关键字)。定义e[i,j]为包含关键字ki..kj的最优二叉搜索中进行一次搜索的期望代价,最终希望计算出e[1,n]j=i-1的情况最简单,子树只包含伪关键字di-1, e[i, i-1] = qi-1j>=i时,需要从ki...kj中选择一个根节点kr,然后构造一颗包含ki...kr-1的最优二叉搜索树做左子树,kr+1...kj的最优二叉搜索树做右子树。

当一颗子树成为一个结点的子树时,每个结点的深度都增加了1,期望搜索代价的增加值应为所有概率之和对于包含ki...kj的子树,所有概率之和为 w(i,j) = (pl)(l从i到j)+ql(l从i-1到j)因此,若kr为包含关键字ki...kj的最优二叉搜索树的根节点,e[i,j] = pr + e[i,r-1]+w(i,r-1)+e[r+1,j]+w(r+1,j),又 w(i,j) = w(i,r-1)+pr+w(r+1,j)所以e[i,j] = e[i, r-1]+e[r+1,j]+w(i,j)直观上就是左子树的右子树的加上增量

qi-i j=i-1  

e[i,j] = 

min{e[i,r-1]+e[r+1,j]+w(i,j)} i<=j

S3:计算最优二叉搜索回溯的期望搜索代价

用 root[i,j]来记录这个子树的根对j>=i w[i,j] = w[i, j-1]+pj+qj伪代码如下:

```Optimal_BST(p,q,n)

{ let e[1..n+1, 0..n], w[1..n+1,0..n] root[1..n,1..n]be new tables;

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

{ e[i,i-1] = qi-1; w[i,i-1] = qi-1;}

for(l=1;l<=n;l++)

for(i=1;i<=n-l+1;i++)

{j = i+l-1;

e[i,j] = 1000000000;

w[i,j] = w[i,j-1]+pj+qj;

for(int r = i;r<=j;r++)

{t = e[i,r-1]+e[r+1,j]+w[i,j];

if(t<e[i,j]) {e[i,j] = t; root[i,j] = r;}

} }

}```

0 0
原创粉丝点击