您的位置:首页 > 其它

算法-动态规划之最长子序列(二)

2014-04-08 22:27 169 查看
       最长公共子序列问题是一道的动态规划算法的常见问题,问题为求两个字符序列的最长公共字符子序列:
       子序列概念:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。假如令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,那么存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
现在有A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。那么有
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
       根据以上三条结论我们可以这么划分子问题:在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。

想起来之前的背包问题了么,我们需要一个二维数组C[n+1][m+1](即table)来存储所有算过的值,用c[i][j]记录X[i]与Y[j]的LCS的长度,有递推式子:



回溯输出最长公共子序列过程:



算法分析:我们需要填满这些空格,所以算法复杂度和空间复杂度都为O(nm)
Java代码如下所示:
package algorithm;

import java.util.Arrays;

public class LongestSubSequence {
private int[][] Memory;

private char[] X = {'0','A','B','C','B','D','A','B'};
private char[] Y = {'0','B','D','C','A','B','A'};

private int LCSLength(int n,int m){
if(Memory
[m] != '0'){
return Memory
[m];
}

if(n == 0 || m == 0){
return 0;
}

if(X
== Y[m]){
Memory
[m] = LCSLength(n-1,m-1)+1;
return 	Memory
[m];
}else{
Memory
[m] = Math.max( LCSLength(n-1,m),LCSLength(n,m-1));
return Memory
[m];
}
}

public int getOPT(int n,int m){
Memory =  new int[n+1][m+1];
for(int i=0;i<n+1;i++){
Arrays.fill(Memory[i],'0');
}
return LCSLength(n,m);
}

public static void main(String[] args) {
System.out.print(new LongestSubSequence().getOPT(7, 6));
}

}


/*
* 如果不用递归地方式,动态规划该怎么样实现呢?
* 其实,仔细研究上面的方法可以发现,不断的递归,最终终止在n=0,m=0处,那么我们就可以嵌套nm循环从n=0,m=0开始遍历即可;
*/
public class LongestSubSequence1 {
private int[][] Memory;
private char[] X, Y;
public LongestSubSequence1(char[] X,char[] Y){
Memory = new int[X.length+1][Y.length+1];
for(int i=0;i<Memory.length;i++){
Arrays.fill(Memory[i], 0);
}
this.X = X;
this.Y = Y;
}
public int getOPT(){
for(int i=1;i<X.length+1;i++){
for(int j=1;j<Y.length+1;j++){
if(X[i-1] == Y[j-1]){
Memory[i][j] = Memory[i-1][j-1]+1;
}else if(Memory[i-1][j] <= Memory[i][j-1]){
Memory[i][j] = Memory[i][j-1];
}else{
Memory[i][j] = Memory[i-1][j];
}
}
}
return Memory[X.length][Y.length];
}
public static void main(String[] args) {
// TODO Auto-generated method stub
char[] X = {'0','A','B','C','B','D','A','B'};
char[] Y = {'0','B','D','C','A','B','A'};

LongestSubSequence1 ma = new LongestSubSequence1(X,Y);
System.out.print(ma.getOPT());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: