Hirschberg的LCS算法实现

article/2025/9/11 11:14:11
解决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.

http://chatgpt.dhexx.cn/article/pFj7Ix4d.shtml

相关文章

SLIC算法

基础知识 在介绍SLIC之前&#xff0c;先来介绍以下Lab颜色空间的介绍。 Lab色彩模型是由亮度(L)要素和与有关色彩的a,b要素组成&#xff0c;L的值由0(黑色)到100(白色)&#xff0c;a表示从洋红色至绿色的范围(a为负值表示绿色而正值表示品红)&#xff0c;b表示从黄色至蓝色的…

动态规划之LCS算法

一、前言 LCS是Longest Common Subsequence的缩写&#xff0c;即最长公共子序列。一个序列&#xff0c;如果是两个或多个已知序列的子序列&#xff0c;且是所有子序列中最长的&#xff0c;则为最长公共子序列。 另外还有个分支问题&#xff1a;最长公共子串。子串的字符位置必…

LCS算法的C++实现

这两天忙里偷闲看了July的团队提供的LCS算法视频&#xff0c;真的如视频标题一样&#xff0c;十分钟搞定LCS算法。 感谢July大神&#xff0c;感谢其团队的邹博。 这里附上视频链接&#xff1a;http://www.julyedu.com/video/play?course17 说是十分钟搞定&#xff0c;其实是…

算法学习 - 最长公共子序列(LCS)C++实现

最长公共子序列 最长公共子序列的问题很简单&#xff0c;就是在两个字符串中找到最长的子序列&#xff0c;这里明确两个含义&#xff1a; 子串&#xff1a;表示连续的一串字符 。子序列&#xff1a;表示不连续的一串字符。 所以这里要查找的是不连续的最长子序列&#xff0c; …

SLIC算法介绍

SLIC&#xff08;simple linear iterativeclustering&#xff09;&#xff0c;即 简单线性迭代聚类 。 &#x1f49b;它是2010年提出的一种思想简单、实现方便的算法&#xff0c;将彩色图像转化为CIELAB颜色空间和XY坐标下的5维特征向量&#xff0c;然后对5维特征向量构造距离度…

LSC算法

1.问题 给定序列 X<x_1,x_2,…,x_m> Y<y_1,y_2,…,y_j> 求X和Y的最长公共子序列(LCS) 2.解析 X<x1,x2,x3,x4…,xi> Y<y1,y2,y3,y4…,yi> 如果Z<z1,z2,z3,z4…,zk>是他们的最长公共子序列 则&#xff1a; &#xff08;1&#xff09;xi yi&…

LCS算法详解

程序员编程艺术第十一章&#xff1a;最长公共子序列(LCS)问题 0、前言 程序员编程艺术系列重新开始创作了&#xff08;前十章&#xff0c;请参考程序员编程艺术第一~十章集锦与总结&#xff09;。回顾之前的前十章&#xff0c;有些代码是值得商榷的&#xff0c;因当时的代码只顾…

LCS 最大公共序列算法

这些天在了解chrome的courgette, 了解了rsync算法, 也了解了courgette使用了bsdiff 算法, 然后知道了bsdiff算法里主要使用的是 LCS 算法, 这里参考了july大牛的文章: http://blog.csdn.net/v_july_v/article/details/6695482 自己做一点概括性的总结, 用以备忘, 也把自…

最长公共子序列(LCS)算法

一、最长公共字串与最长公共子序列 最长公共子串&#xff08;Longest Common Substirng&#xff09; 子串是串的一个连续的部分&#xff0c;子串中字符的位置必须连续。 例如&#xff1a;有两个字符串ABCBDAB 和 BDCABA&#xff0c;则它们的最长公共子串是&#xff1a;AB。 …

LCS(longest common sequence)算法的实现(十分详细)

一、问题描述 有两个字符串&#xff0c;求二者的最长公共子序列。 最长公共子序列&#xff1a;不必连续但必须有序的子列&#xff08;与子串区分&#xff0c;子串是连续的&#xff09; 二&#xff1a;解决方法 第一种方法&#xff1a;穷举法 &#xff0c;就是一个一个的对比&a…

LCS算法

LCS算法 LCS算法&#xff1a; LCS是Longest Common Subsequence的缩写&#xff0c;即最长公共子序列。一个序列&#xff0c;如果是两个或多个已知序列的子序列&#xff0c;且是所有子序列中最长的&#xff0c;则为最长公共子序列。LCS不是唯一的&#xff0c;它可以有很多种&am…

Oracle中索引的原理

索引的概念 索引是一种数据库结构&#xff0c;能够就数据库中的某列提供快速查询&#xff0c;而不用检索整个表格&#xff08;官方的不行&#xff09;。 在 Oracle 数据库中&#xff0c;存储的每一行数据都有一个 rowID 来标识。当 Oracle 中存储着大量的数据时&#xff0c;意…

MongoDB索引原理及实践

背景 数据库的演进 随着计算机的发展&#xff0c;越来越多的数据需要被处理&#xff0c;数据库是为处理数据而产生。从概念上来说&#xff0c;数据库是指以一定的方式存储到一起&#xff0c;能为多个用户共享&#xff0c;具有更可能小的冗余&#xff0c;与应用程序彼此独立的…

MySql存储引擎和索引原理

转载 https://blog.csdn.net/tongdanping/article/details/79878302 注意&#xff1a; 1、索引需要占用磁盘空间&#xff0c;因此在创建索引时要考虑到磁盘空间是否足够 2、创建索引时需要对表加锁&#xff0c;因此实际操作中需要在业务空闲期间进行 MySQL支持诸多存储引擎&a…

MySQL之索引原理

1 简介 索引底层就是一种数据结构&#xff0c;空间换时间&#xff0c;能够帮助我们快速定位到对应的数据&#xff0c;就类似于字典里面的目录一样。 索引虽然能快速检索数据&#xff0c;但会影响数据修改的操作&#xff0c;而且索引存储在具体的文件&#xff0c;占用一定的空…

深入浅出数据库索引原理

使用索引很简单&#xff0c;只要能写创建表的语句&#xff0c;就肯定能写创建索引的语句&#xff0c;要知道这个世界上是不存在不会创建表的服务器端程序员的。然而&#xff0c; 会使用索引是一回事&#xff0c; 而深入理解索引原理又能恰到好处使用索引又是另一回事&#xff0…

MySQL索引原理和实现

说到索引&#xff0c;很多人都知道“索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff0c;在数据十分庞大的时候&#xff0c;索引可以大大加快查询的速度&#xff0c;这是因为使用索引后可以不用扫描全表来定位某行的数…

倒排索引原理,即为什么叫倒排索引

倒排索引的英文原名是Inverted index&#xff0c;大概因为Invert有颠倒的意思&#xff0c;所以就被翻译成了倒排&#xff0c;然后我们就会在字面上出现误解&#xff1a;理解为从A-Z颠倒成Z-A。其实它并不是字面上的意思。 倒排索引源于实际应用中需要根据属性的值来查找记录&a…

【数据库】数据库索引原理

正确的创建合适的索引 是提升数据库查询性能的基础 文章目录 1.索引是什么&#xff1f;2.为什么&#xff1f;3.索引原理B tree 4.B tree 在两大引擎中的体现5.索引的原则 1.索引是什么&#xff1f; 索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构。 2.为…

Mysql数据库的索引原理

写在前面&#xff1a;索引对查询的速度有着至关重要的影响&#xff0c;理解索引也是进行数据库性能调优的起点。考虑如下情况&#xff0c;假设数据库中一个表有10^6条记录&#xff0c;DBMS的页面大小为4K&#xff0c;并存储100条记录。如果没有索引&#xff0c;查询将对整个表进…