Hirschberg的LCS算法实现

原创 2007年10月10日 15:05:00
解决Longest Common Subsequence(LCS)问题最常用的算法是Dyanmic programing,细节可以参考Ch15.4 of Introduction of Algorithm(2ED), MIT press, p 350。这个算法最大的问题是他的空间复杂度是O(m*n)。这样,当两个序列达到上万个节点时,内存消耗就成为了大问题。
1975年D. S. Hirschberg提出了另外一种算法,他的时间复杂度略高于Dynamic programing,但是,空间复杂度只有O(m+n),可以很好的解决大序列的LCS问题。参见D. S. Hirschberg. A linear space algorithm for computing maximal common subsequences. Comm. A.C.M. 18(6) p341-343, 1975.
下面给出这个算法的C++和Python实现。
原算法中使用的序列下表从一开始,在此根据编程语言的特点做了优化,改成了从0开始,所以和原始算法看上去有差异。
C++(VS2005下编译通过)

 
#include <vector>
#include 
<algorithm>
using namespace std;

vector
< int > findRow0(int m, int n, vector<TCHAR> A, vector<TCHAR> B)
...{
    vector
<int> K0; 
    vector
<int> K1(n+10);
    
//# in PDF, this lien is 1:n, It may be wrong
    forint i = 0; i<m; i++)
    
...{
        K0 
= K1;
        
for(int j = 0; j < n; j++)
        
...{
            
if (A[i] == B[j])
                K1[j
+1= K0[j] +1;
            
else
                K1[j
+1= max ( K1[j], K0[j+1]);
        }

    }

    vector
<int> LL = K1;
    
    
return LL;

}


vector
<TCHAR> H_LCS0(int m, int n, vector<TCHAR>  A, vector<TCHAR> B)
...{
    
    vector
<TCHAR> C;
    
if (n == 0)
        C.clear(); 
    
else if (m == 1)
    
...{
        vector
<TCHAR>::iterator result = find( B.begin( ), B.end( ), A[0] );
        
if  ( result != B.end( ) )

        
//if A[0] in B:
            C = vector<TCHAR>(1,  A[0]);
        
else
            C.clear(); 
    }

    
else
    
...{
        
int i = m / 2;
        
//#step3 
        
        vector 
<TCHAR> A1i(A.begin(),A.begin()+i);
        vector
<int> L1 = findRow0(i, n, A1i, B);
        
        
        vector 
<TCHAR> Anip1(A.rbegin(), A.rend()-i);
        
        vector
< TCHAR > Bn1(B.rbegin(), B.rend());
        
        vector
<int> L2 = findRow0(m-i, n, Anip1, Bn1);
        
        
//#step4
        int M = 0;
        
int k = 0;
        
        
for ( int j = 0; j<=n; j++)
        
...{
            
int tmp = L1[j] + L2[n-j];
            
if (tmp > M)
            
...{
                M 
= tmp;
                k 
= j;
            }

        }

        
        
//#step 5
        vector< TCHAR > A0i(A.begin(), A.begin()+i);
        vector
< TCHAR > B0k(B.begin(), B.begin()+k);
        vector
< TCHAR > C1 = H_LCS0( i, k, A0i, B0k); 
        
        vector
< TCHAR > Aim(A.begin()+i, A.end());
        vector
< TCHAR > Bkn(B.begin()+k, B.end());
        vector
< TCHAR > C2 = H_LCS0( m-i, n-k, Aim, Bkn);


        
//#step 6
        C = C1;
        C.insert(C.end(), C2.begin(), C2.end());
    }

    
    
return C;
}


    

int _tmain(int argc, _TCHAR* argv[])
...{
    
if(argc < 3) _tprintf(_T("At least need two string "));
    
else
    
...{
        
int m = _tcslen(argv[1]);
        vector 
<TCHAR> A(argv[1], argv[1+ m);
        
int n = _tcslen(argv[2]);
        vector 
<TCHAR> B(argv[2], argv[2+ n);
        vector 
<TCHAR> C = H_LCS0(m, n, A, B);
        C.push_back(
0);
        _tprintf(
&C[0]);

    }

    
return 0;
}


Python 代码(在python2.5下测试)
def findRow0(m, n, A, B):
    
print "findRow0", m , n , ''.join(A), ''.join(B)
    K0 
= []
    K1 
= [0] * (n+1)
    
# in PDF, this lien is 1:n, It may be wrong
    for i in range(0,m):
        K0 
= K1[:]
        
for j in range(0,n):
            
#print i, j
            if A[i] == B[j]:
                K1[j
+1= K0[j] +1
            
else:
                K1[j
+1= max ( K1[j], K0[j+1])
        
    LL 
= K1
    
print 'LL =', LL
    
return LL

def H_LCS0(m, n, A, B):
    
print "H_LCS0", m, n, ''.join(A), ''.join(B)
    
if n == 0:
        C 
= []
    
elif m == 1:
        
if A[0] in B:
            C 
= [A[0]]
        
else:
            C 
= []
    
else:
        i 
= m / 2
        
#step3 
        L1 = []
        A1i 
= A[0:i]
        L1 
= findRow0(i, n, A1i, B)
        
        
        Anip1 
= A[i:]
        Anip1.reverse()
        Bn1 
= B[:]
        Bn1.reverse()
        L2 
= findRow0(m-i, n, Anip1, Bn1)
        
        
#step4
        M = 0
        k 
= 0
        
for j in range(0, n+1):
            tmp 
= L1[j] + L2[n-j]
            
if tmp > M:
                M 
= tmp
                k 
= j
        
        
#step 5
        print 'i=', i, 'k=', k, 'm=', m, 'n=', n
        C1 
= H_LCS0( i, k, A[0:i], B[0:k])
        
        C2 
= H_LCS0( m-i, n-k, A[i:], B[k:])
        
#step 6
        C = C1+ C2
        
print "C1=",  ''.join(C1), "C2="''.join(C2), 
    
print "C = "''.join(C)
    
return C

= "ACGTACGTACGT"
= "AGTACCTACCGT"
= H_LCS0(len(A), len(B), list(A), list(B))
print "final result"''.join(C)
代码还有很多可以优化的地方。
另外,发现还有一些类似的算法,特别python的difflib采用的算法,找出的不一定是理论上的最长子序列。特别是在序列中相同元素重复出现次数比较高的时候特别明显。猜测,可能和他采用了对元素进行hash造成的。另外,他的文档中也说明:This does not yield minimal edit sequences, but does tend to yield matches that ``look right'' to people. (4.4 difflib -- Helpers for computing deltas of Python Library Reference for python 2.5)
具体算法可以参见
Pattern Matching: The Gestalt Approach
Discussion of a similar algorithm by John W. Ratcliff and D. E. Metzener. This was published in Dr. Dobb's Journal in July, 1988.

LCS算法的python实现

''' Created on 2012-11-9 @author: Pandara ''' def lcs_len(a, b): ''' a, b: strings ''' ...
  • qq917141931
  • qq917141931
  • 2012-11-10 14:46:12
  • 1813

最长公共子序列python实现

最长公共子序列是动态规划基本题目,下面按照动态规划基本步骤解出来。 1.找出最优解的性质,并刻划其结构特征 序列a共有m个元素,序列b共有n个元素,如果a[m-1]==b[n-1],那么a[:m]和b...
  • littlethunder
  • littlethunder
  • 2014-05-12 17:05:26
  • 10812

LCS算法的C++实现

最长公共子序列LCS的C++实现
  • zmq570235977
  • zmq570235977
  • 2015-11-15 15:02:40
  • 1813

LCS算法的两种JAVA实现方式

给定字符串A,B Solution  I: 1.构造数组 c i  j 描述A串的前i位和B串的前J位的LCS长度 2.构造数组 trace  i  j  描述max相应位置得到的长度是由哪一步得出的...
  • Lyz1052
  • Lyz1052
  • 2015-06-14 12:39:30
  • 878

编程算法 - 最长公共子序列(LCS) 代码(C)

最长公共子序列(LCS) 代码(C)本文地址: http://blog.csdn.net/caroline_wendy题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符串的...
  • u012515223
  • u012515223
  • 2014-07-17 21:10:46
  • 3067

动态规划算法求lcs(最长公共子串)之Java代码实现

LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。 比如:   String str1 = new String("adbccadebbca");...
  • earbao
  • earbao
  • 2015-12-29 11:17:09
  • 4282

LCS算法

首先将要看到如何运用动态编程查找两个 DNA 序列的最长公共子序列(longest common subsequence,LCS)。发现了新的基因序列的生物学家通常想知道该基因序列与其他哪个序列最相似...
  • leohxj
  • leohxj
  • 2010-11-11 19:59:00
  • 13864

[差量更新系列1]BSDiff算法学习笔记

[差量更新系列1]BSDiff算法描述     BSDiff是一个差量更新算法,它在服务器端运行BSDiff算法产生patch包,在客户端运行BSPatch算法,将旧文件和patch包合成新...
  • add_ada
  • add_ada
  • 2016-04-24 12:12:22
  • 7609

LCS 最大公共序列算法

这些天在了解chrome的courgette,  了解了rsync算法,  也了解了courgette使用了bsdiff 算法,  然后知道了bsdiff算法里主要使用的是 LCS 算法, 这里参考了...
  • dylgsy
  • dylgsy
  • 2012-11-28 19:00:45
  • 24382

Android 增量更新 bsdiff bspatch

最初的增量更新应该是Google Play在Google IO 2012 上提出来的,现在被各大应用市场广泛使用。 最近我们也需要用到这个技术,所以研究一下下~ http://k...
  • song_shi_chao
  • song_shi_chao
  • 2014-03-13 17:20:25
  • 10841
收藏助手
不良信息举报
您举报文章:Hirschberg的LCS算法实现
举报原因:
原因补充:

(最多只允许输入30个字)