Hirschberg的LCS算法实现

解决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.
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值