您的位置:首页 > 其它

动态规划实现最长公共子序列(LCS)算法

2008-01-28 13:35 405 查看

实验目的:

熟悉并实现最长公共子序列算法,理解动态规划算法的核心思想。

问题定义

输入任意两个字符串,求其最长公共子序列。
输入格式:两条随机序列,如 1 3 4 5 5 and 2 4 5 5 7 6
输出格式:它们的最长公共子序列,例:4 5 5
最小规模:程序可处理的序列长度不得小于100

实验思想

记 Xi=﹤x1,⋯,xi﹥即X序列的前i个字符 (1≤i≤m)(前缀)
Yj=﹤y1,⋯,yj﹥即Y序列的前j个字符 (1≤j≤n)(前缀)
假定Z=﹤z1,⋯,zk﹥∈LCS(X , Y)。
若xm=yn(最后一个字符相同),则不难用反证法证明:该字符必是X与Y的任一最长公共子序列Z(设长度为k)的最后一个字符,即有zk = xm = yn 且显然有Zk-1∈LCS(Xm-1 , Yn-1)即Z的前缀Zk-1是Xm-1与Yn-1的最长公共子序列。
若xm≠yn,则亦不难用反证法证明:要么Z∈LCS(Xm-1, Y),要么Z∈LCS(X , Yn-1)。
由于zk≠xm与zk≠yn其中至少有一个必成立,若zk≠xm则有Z∈LCS(Xm-1 , Y),类似的,
若zk≠yn 则有Z∈LCS(X , Yn-1)
若xm=yn,则问题化归成求Xm-1与Yn-1的LCS(LCS(X , Y)的长度等于LCS(Xm-1 , Yn-1)的长度加1)
若xm≠yn 则问题化归成求Xm-1与Y的LCS及X与Yn-1的LCS
LCS(X , Y)的长度为:max{LCS(Xm-1 , Y)的长度, LCS(X , Yn-1)的长度}求LCS(Xm-1 , Y)的长度与LCS(X , Yn-1)的长度
这两个问题不是相互独立的:
两者都需要求LCS(Xm-1,Yn-1)的长度,另外两个序列的LCS中包含了两个序列的前缀的LCS,故问题具有最优子结构性质 考虑用动态规划法。

引进一个二维数组C,用C[i,j]记录Xi与Yj的LCS的长度如果我们是自底向上进行递推计算,那么在计算C[i,j]之前,C[i-1,j-1], C[i-1,j]与C[i,j-1]均已计算出来。此时我们
根据X[i]=Y[j]还是X[i]≠Y[j],就可以计算出C[i,j]:
若X[i]=Y[j],则执行C[i,j]←C[i-1,j-1]+1;若X[i]≠Y[j],则根据:
C[i-1,j]≥C[i,j-1],则C[i,j]取C[i-1,j];否则C[i,j]取C[i,j-1]。

为了构造出LCS,使用一个m×n的二维数组b,b[i,j]记录C[i,j]是通过哪一个子问题的值求得的,以决定搜索的方向:
若C[i-1,j]≥C[i,j-1],则b[i,j]中记入“↑”;
若C[i-1,j] < C[i,j-1],则b[i,j]中记入“←”;
为节省空间,数组b亦可不用,直接根据X[i]=Y[j]还是X[i]≠Y[j]以及C[i,j-1],C[i-1,j]来找出搜索方向:
X[i]=Y[j]等价于b[i,j]=“↖”,
X[i]≠Y[j]且C[i,j-1] > C[i-1,j] 等价于b[i,j]=“←”,
X[i]≠Y[j]且C[i,j-1] < C[i-1,j] 等价于b[i,j]=“↑”,
X[i]≠Y[j]且C[i,j-1] = C[i-1,j] 等价于b[i,j]=“←↑”。

整个算法的时间复杂度为O(M*N),算法至少需要M*N的空间。

测试数据及结果

本测试的硬件以及软件环境如下
CPU:PM 1.5G; 内存:768M;操作系统:windows xp sp2;软件平台:JDK1.5;开发环境:eclipse
如图1所示:即为求2个长度为100的随机字符串的最长公共子序列的算法结果。



图1

实验结论以及算法分析

通过测试证明算法正确有效。
性能分析的方法:使用JDK 1.5的System.nanoTime(),计算算法消耗的时间,以此来评价算法。(该方法在JDK1.5以下的版本中不支持)
为了不影响算法的准确度,在测试的过程我们注释掉了打印随机字符串的步骤,仅仅计算对这两个随机字符串求最长公共子序列的功能。

图2

图2 蓝线表示输入随机字符串在等长的情况下,n增大时求最长公共子序列对所消耗的时间,该趋势随着指数增加。



图3(单位ms)
图3表示字符串1的长度为100,随着字符串2的的长度增加程序运行的时间消耗。该趋势趋近于线性增长的。
图2和图3表明该程序的时间复杂度与理论分析相符合。

源代码

最长公共子序列算法(java描述)
LCS.Java


package lcs;




import java.util.Random;






public class LCS ...{




public static void main(String[] args) ...{




//设置字符串长度


int substringLength1 = 100;


int substringLength2 = 100;




// 随机生成字符串


String x = GetRandomStrings(substringLength1);


String y = GetRandomStrings(substringLength2);




Long startTime = System.nanoTime();


// 构造二维数组记录子问题x[i]和y[i]的LCS的长度


int[][] opt = new int[substringLength1 + 1][substringLength2 + 1];




// 动态规划计算所有子问题




for (int i = substringLength1 - 1; i >= 0; i--) ...{




for (int j = substringLength2 - 1; j >= 0; j--) ...{


if (x.charAt(i) == y.charAt(j))


opt[i][j] = opt[i + 1][j + 1] + 1;


else


opt[i][j] = Math.max(opt[i + 1][j], opt[i][j + 1]);


}


}




//System.out.println("substring1:"+x);


//System.out.println("substring2:"+y);


//System.out.print("LCS:");




int i = 0, j = 0;




while (i < substringLength1 && j < substringLength2) ...{




if (x.charAt(i) == y.charAt(j)) ...{


System.out.print(x.charAt(i));


i++;


j++;


} else if (opt[i + 1][j] >= opt[i][j + 1])


i++;


else


j++;


}


Long endTime = System.nanoTime();


System.out.println(" Totle time is " + (endTime - startTime) + " ns");


}




//取得定长随机字符串




public static String GetRandomStrings(int length) ...{


StringBuffer buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyz");


StringBuffer sb = new StringBuffer();


Random r = new Random();


int range = buffer.length();




for (int i = 0; i < length; i++) ...{


sb.append(buffer.charAt(r.nextInt(range)));


}


return sb.toString();


}


}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: