zoukankan      html  css  js  c++  java
  • 基于位运算的最长公共子串算法

    本文章来源

    [常规动态规划算法]

    L[i , j]等于A[1..i] , B[1..j]LCS.

    则有L[i,j] = 1 + L[i-1 , j-1]                    如果 (A[i] = B[j])

                  Max(L[i-1 , j] , L[i , j-1])   其他

    复杂度为O(|A| * |B|)

    [基于位运算的动态规划算法]

    根据上面的动态规划算法,状态函数L具有如下性质:

    L[i-1,j-1] ≤ L[i,j-1] , L[i-1,j] ≤ L[i,j]

      | L[i,j]-L[i-1,j-1] | ≤ 1

      对于L的每一行,相邻的两个元素的最多只相差1。这样一来,我们就可以用一个二进制的矩阵描述出L :    

    #bits

     9    0 0 0 1 1 0 0 0 1 0 1 1 1 1 1 1 | T   string B

     9    0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 1 | T 

     8    0 0 1 0 0 0 1 0 0 0 1 1 1 1 1 1 | C

     7    0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 | T   - Row[11]

     7    1 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 | A   - Row[10]

     7    1 0 0 0 0 1 0 0 0 0 0 1 1 1 1 1 | G

     6    0 0 0 0 0 1 0 1 0 0 0 0 1 1 1 1 | A

     5    0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 | A

     5    0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 | T

     4    0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 | T

     3    0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 | C

     3    1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 | G

     2    0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 | A

     1    0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 | T

         _________________________________.        matrix Mij

    String A:  G T C T T A C A T C C G T T C G 

     

    这里,我们将串A从右往左写,串B从下往上写。Row[i]中的1的个数总是和Row[i-1]中的1的个数一样多或者恰好多一个串A和串B的LCS即为最上面一行Row[|B|]中1的个数

    字符比较串表

    这里我们定义一组称为字符比较串的二进制串。分别是字符集中的每一个字符与串A的比较结果(相同为1,不同为0)

    AG T C T T A C A T C C G T T C G

    ‘A’-string: 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0

    ‘C’-string: 0 0 1 0 0 0 1 0 0 1 1 0 0 0 1 0

    ‘G’-string: 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1

    ‘T’-string: 0 1 0 1 1 0 0 0 1 0 0 0 1 1 0 0

    预先计算这个字符比较串表,时间复杂度为O(|S|*|A|)。对于一个确定的字符集,时间复杂度为O(|A|)。对于一个不确定的字符集,最坏情况为O(|A|*|B|)。如果字符集较小,|S|<<|B|,预处理的时间复杂度可以被忽略。

    矩阵M

    为了计算Row[i],我们需要用到字符比较串表中的B[i]-string。以Row[10]Row[11]为例,我们来研究如何计算出Row[i]

    下面是Row[10] , 以及B[10]-string(‘T’-string). 按照Row[10]中的1分隔:

    Row[10]: 1 0 0 0 0 0 0   1 0 0 0   1   1   1   1   1

    T-string : 0 1 0 1 1 0 0   0 1 0 0   0   1   1   0   0

    每一段都是Row[i-1]的一个1的位开始往右延伸,直到下一个位置是1或者串结束。如果Row[i-1]的最左边的位置上是0,那么最左边的一段从B[i]-string的最左边的1的位置开始延伸直到下一个1Row[i]的构成方式很简单:就是对于每一段,都是选择Row[i-1]或者B[i]-string最右边的1所在位置为1,其他的为0。如果这一段Row[i-1]和B[i]-string都是0,那么Row[i]这一段也为0

    ‘T’-string Or Row[10]:

      1 1 0 1 1*0 0    1 1*0 0   1*  1* 1*  1*  1*

    Row[11]: 0 0 0 0 1 0 0    0 1 0 0   1   1   1   1   1

    *表示了进行或操作之后每一段最右边的1

    附带一提,你可以假定在每个串的最左边(位置|A|+1)存在一个1,这样可以方便处理最左边一段全为0的情况。不过对于本算法并没有这个必要。

    Row[i-1]中的一个1的位置,代表了A中的一个最短前缀与B[1..i-1]LCS达到了该长度。引进B[i],最好的方法当然是在前面的最短基础上加入一个最短的与B[i]的匹配,也就是从那个1所在的位置往左找,找到的第一个能与B[i]匹配的(如果找得到的话)

    X = Row[i-1] Or B[i]-string

    对于当前的例子,X = Row[10] Or ‘T’-string

        X:  1 1 0 1 1 0 0    1 1 0 0   1   1   1   1   1

    Row[i-1]往左移1位,并且设最低位为1,用X去减这个串:

    X:         1 1 0 1 1 0 0    1 1 0 0   1   1   1   1   1

             - 0 0 0 0 0 0 1    0 0 0 1   1   1   1   1   1

               -------------    -------   -   -   -   -   -

               1 1 0 1 0 1 1    1 0 1 1   0   0   0   0   0

    这个操作所表达的意思为对于每一段,把最右边的一个1变成0,把这个1右边的所有0变成1,设最低位为1的目的是为了可以同样处理最后一段

    再跟X 进行Xor(异或)操作,得到:

    0 0 0 0 1 1 1    0 1 1 1   1   1   1   1   1

    这步操作之后的结果就是,对于每一段,从最右边的1开始到段尾变成1,其他变成0。因为Xor为1当且仅当两个位上的数不同,而上一次操作我们修改过的位是从最右边的1直到段尾

    接下来就很显然了,只要再将上面的结果与X进行And操作即可。得到:

    0 0 0 0 1 0 0    0 1 0 0   1   1   1   1   1

    这样就得到了Row[i] , 即当前例子中的Row[11]

    综上,

    Row[i] = X And ( (X – ((Row[i-1] << 1) + 1)) Xor X),   

    其中X = Row[i-1] Or B[i]-string

    算法到此结束。计算出Row[|B|]之后,数一下其中有多少个1的位,便是答案了。

  • 相关阅读:
    linux下好用软件全记录
    Shell之Here Document
    ThinkPHP判断更新是否成功的正确方法
    memcached单点登录配置
    gedit搭建c开发环境
    两种算法的比较,学习算法的重要性
    解决sendmail发送邮件慢的问题
    直接拿来用,10个PHP代码片段(收藏)
    Nginx 简单的负载均衡配置示例
    Nginx 虚拟主机 VirtualHost 配置
  • 原文地址:https://www.cnblogs.com/juruohx/p/7768179.html
Copyright © 2011-2022 走看看