求最长公共子序列(LCS)

来源:互联网 发布:用友软件增加固定资产 编辑:程序博客网 时间:2024/05/22 12:36

本总结是是个人为防止遗忘而作,不得转载和商用。

LCS的定义

         其实看字面意思就知道LCS是什么了,这里主要是提醒注意区别最长公共子串。

                   最长公共子序列不要求连续

                   最长公共子串不要求连续

         如:

                   字符串13455与245576的最长公共子序列为455,最长子串也是455。

                   字符串acdfg与adfc的最长公共子序列为adf,没有子串子串。

题目

         求两个字符串的LCS。

记号

         为了方便说明,这里先做一些记号:

                   字符串X,长度为m,从1开始数;

                   字符串Y,长度为n ,从1开始数;

                   Xi=﹤x1,⋯,xi﹥:X序列的前i个字符 (1≤i≤m)

                   Yj=﹤y1,⋯,yj﹥:Y序列的前j个字符 (1≤j≤n)

                   LCS(X, Y) :字符串X和Y的最长公共子序列,即:

                            Z=﹤z1,⋯,zk﹥。

                            注:这是不严格的表述。事实上,X和Y的可能存在多个子串,长度相同并且最大,因此,LCS(X,Y)严格的说,是个字符串集合。即:Z∈ LCS(X , Y) .

分析

         假设我们已经求出了Xm-1和Yn-1的LCS,即,求出了LCS(Xm-1,Yn-1),那如何求LCS(Xm, Yn)呢?

         这要分情况讨论:

                   情况1:X的第m个字符xm= Y的第n个字符yn

                                          此时:LCS(Xm,Yn) = LCS(Xm-1, Yn-1) + xm

                            如:

                            对于上面的字符串X和Y:

                                     x3 =y3 =‘C’,则:LCS(BDC,ABC)=LCS(BD,AB)+‘C’

                                     x5 =y4=‘B’,则:LCS(BDCAB,ABCB)=LCS(BDCA,ABC)+‘B’

                   情况2:X的第m个字符xm≠ Y的第n个字符yn

                                          此时:

                                     要么:LCS(Xm,Yn) = LCS(Xm-1, Yn)

                                     要么:LCS(Xm,Yn) = LCS(Xm, Yn-1)

状态转移方程

         根据上面的说明,就可以写出状态转移方程了,如下:

                   xm= ym

                            LCS(Xm, Yn)= LCS(Xm-1, Yn-1) + xm

                   xm≠ yn

                            LCS(Xm,Yn) = max( LCS(Xm-1, Yn),LCS(Xm, Yn) = LCS(Xm, Yn-1) )

实现

         它的实现一般采用如下的方式。

         使用一个二维数组C[m,n],其中C[i, j] 记录序列Xi和Yj的最长公共子序列的长度。

                   PS:当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故C[i,j]=0。

         因此状态转移方程可以写成:

                   i = 0或j = 0时

                            C[i,j] = 0

                   i > 0, j > 0,且xi = yj

                            C[i,j] = C[i-1, j-1] + 1

                   i > 0, j > 0,且xi ≠ yj

                            C[i,j] = max( C[i-1, j], C[i, j-1] )

例子

        

         如上图所示:

                   X= <A, B, C, B, D, A, B>,Y = <B, D, C, A, B, A>,方格中的数字时该位置的LCS[Xi,Yj]。

         首先,第0行第0列都赋值为0,因为上面已经说了:i = 0或j = 0时,C[i, j] = 0

         假设现在求LCS[X4,Y3],如箭头所指。

         因为此时i=4>0,j=3>0,且x4≠y3,所以:

                   LCS[X4,Y3] = max(LCS(X3, Y3), LCS(X4, Y2))= max(1, 2) = 2。

         接下来的就是遍历完所有的i,j就有了上面的表的内容。

         剩下的,想求谁的LCS则直接从表中取就是。

如何求LCS的内容

         上面时求LCS的长度,那如果相求LCS的内容呢?

         这样做:

                   刚才是从i=0, j=0遍历到i=7, j=6,那现在就从i=7,j=6往回查找,查找的方式如下:

                            1,将当前的字母追加至保存用的字符串,然后判断:

                                    如果i或j等于0,反转保存用的字符串并返回;

                                    反之,执行第二步。

                            2,查看左边和上边的值:

                                     如果xi≠ yj

                                             如果LCS(Xi-1,Yj) = LCS(Xi, Yj-1):向上走,然后回到第1步

                                             如果LCS(Xi-1,Yj) ≠LCS(Xi, Yj-1):走到值大的位置,然后回到第1步。

                                     如果xi= yj:走到当前位置的左上角,然后回到第1步。     

         PS:如果在“xi≠ yj 时的如果LCS(Xi-1, Yj) = LCS(Xi, Yj-1)”的情况下选择向左走,则会得到另一个结果,但这也是对的,所以LCS(X, Y)有多个解,当然LCS的长度都一样。

             

时间复杂度

         上面是打了一个m*n的表,所以时间复杂度和空间复杂度是O(m*n)。

         不过,如果仅仅是求LCS的长度而不用求LCS本身时,则不需要保存这个表,因为我们计算第i行时最多只使用第i-1行的数据,用不到i-2行的数据,所以只需要每次保存当前行以供下次计算就好,也就是使用滚动数组的方式。

         所以,如果仅仅是求LCS的长度而不用求LCS本身时可以使用用滚动数组:

                  1,  创建个长度是7的数组,并将其初始化为[0,0,0,0,0,0,0],这是第一行

                  2,  到第二行时,该行对应的字母是A,然后让A和每一列进行比对:

                            第一个因为是第一列,所以还强制为0

                            第二列:因为A != B,所以L22= max(L12, L21)

                            …

                            第五列:因为 A = A,所以 L24= L13 + 1

                            …

                   就这样递归

         这样空间复杂度就是O(m)。

 

 

 

                  

 

         

0 0