动态规划实现最长公共子序列(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();
}
}
相关文章推荐
- 算法之动态规划(LCS最长公共子序列, edit distance,交叉子串)
- 【算法之动态规划(三)】动态规划算法之:最长公共子序列 & 最长公共子串(LCS),字符串相似度算法
- 基于动态规划的最长公共子序列实现(LCS)
- 转【算法之动态规划(三)】动态规划算法之:最长公共子序列 & 最长公共子串(LCS)&字符串相似度算法
- 算法:动态规划——最长公共子序列(LCS)
- 动态规划(dynamic program)&& 最长公共子序列(LCS)
- 动态规划---LCS问题 最长公共子序列
- 动态规划_最长公共子序列(LCS)
- 最长公共子序列(LCS) (动态规划算法实现)算法导论p211
- 算法学习心得——动态规划法实现最长公共子序列(LCS)
- 动态规划-最长公共子序列的备忘录算法
- C++实现动态规划求解最长公共子序列
- 最长公共子序列(LCS)动态规划解题笔记
- 动态规划之最长公共子序列(LCS)
- 最长公共子序列LCS(动态规划基础)
- 算法学习 - 最长公共子序列(LCS)C++实现
- 【经典问题】二维动态规划问题:求最长公共子序列LCS
- 动态规划之最长公共子序列(LCS)
- 动态规划解最长公共子序列问题LCS(二)
- 算法起步之动态规划LCS