算法-动态规划之最长子序列(二)
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的长度,有递推式子:
![](https://img-blog.csdn.net/20140408222647234?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpemk4ODg4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
回溯输出最长公共子序列过程:
![](https://img-blog.csdn.net/20140408222701500?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpemk4ODg4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
算法分析:我们需要填满这些空格,所以算法复杂度和空间复杂度都为O(nm)
Java代码如下所示:
/*
* 如果不用递归地方式,动态规划该怎么样实现呢?
* 其实,仔细研究上面的方法可以发现,不断的递归,最终终止在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());
}
}
子序列概念:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。假如令给定的字符序列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());
}
}
相关文章推荐
- 算法笔记:动态规划求解最长子序列问题
- 单调递增最长子序列 分类: 动态规划 算法 2014-10-29 13:11 74人阅读 评论(0) 收藏
- [算法导论]动态规划---最长公共最序列问题
- leetcode 300. Longest Increasing Subsequence-最长子序列|动态规划
- 单调递增最长子序列(动态规划)
- 动态规划解决最长子序列问题
- 最长子序列--动态规划
- 【算法学习笔记】72.LCS 最大公公子序列 动态规划 SJTU OJ 1065 小M的生物实验1
- HDU1950,单调递增最长子序列(n*lgn)算法
- NYOJ-单调递增最长子序列(两种算法)
- 最长子序列和回文字符串(动态规划)
- 夕拾算法进阶篇:12)出栈序列统计(动态规划DP)
- 动态规划,最长回文子序列(java实现),输入character,算法应该返回carac
- 最长上升子序列(LIS)的O(nlogn) & O(n^2)算法 - 动态规划
- 算法-计算递增最大子序列长度(动态规划)
- 【动态规划】LIS最长单调递增子序列 logn算法 并且输出子序列
- NYOJ-单调递增最长子序列(动态规划)
- 动态规划-单调递增最长子序列(三)
- 算法_动态规划_最长单调递增子序列问题(O(nlogn)的时间复杂度)
- 【动态规划】单调递增最长子序列