最长公共子序列及Python实现


1 最长公共子序列问题描述

一个给定序列的子序列是在该序列中删除若干元素后得到的序列,确切的说,若给定序列X = {x1,x2,...xm},则另一个序列,Y= {y1,y2...,yn},当另一个序列即是X的子序列又是Y的子序列时,称Z是序列X也Y的公共子序列。最长公共子序列问题为给定序列X和Y,找到所有公共子序列中最长的一个(非连续)。

2 最长公共子序列的结构

解最长公共子序列问题的最容易想到的方法是穷举法,即对X的所有子序列,检查它是否也是Y 的子序列,从而确定它是否为X和Y 的公共子序列,并且在检查过程中记录最长的公共子序列。X的所有子序列检查过后即可求出X和Y 的最长公共子序列。X的没个子序列相应于下标集{1,2....m}的一个子集。因此,公有2**m个不同的子序列,从而穷举法需要指数时间。

事实上,最长公共子序列问题具有最优子结构性质:

设序列X={x1,x2,....xm}和Y= {y1,y2...,yn}的一个最长公共子序列为Z={z1,z2,...zk},则

(1) 若xm=yn ,则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列。

(2)若xm≠yn,且zk≠xm,则z是Xm-1和Y的最长公共子序列。

(3)若xm≠yn且zk≠yn,则Z是X和Yn-1的最长公共子序列。

证明略

3 子问题的递归结构

由最长公共子序列问题的最优子结构性质可知,要找出X={x1,x2,....xm}和Y={y1,y2...yn}的最长公共子序列,可以按照以下方式递归的进行:当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm(=yn)即可得到X和Y的一个最长公共子序列,当xm≠yn时,必须接两个字问题,也就是要找出Xm-1和Yn的一个最长公共子序列已经Xm和Yn-1的一个最长公共子序列。并求两个子问题公共子序列最长的。用c[i][j]记录序列的Xi和Yj的最长公共子序列的长度,其中Xi={x1,x2...xi};Yj={y1,y2...yj},当i=0或j=0时,空序列是Xi和Yj的最长公共子序列故此时c[i][j]=0,由最优子结构性质可建立递归关系如下:

\

4 计算最优值

直接利用递归关系式容易写出一个计算c[i][j]的递归算法,但其计算时间是随输入长度指数增长的,由于在考虑的子问题空间中,总共有Φ(mn)个不同的子问题,因此,用动态规划算法由底向上计算最优值能提高算法的效率。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+vMbL49fus6S5q7my19PQ8sHQs6S2yLXEtq/MrLnmu67L47eoTENTTGVuZ3Ro0tTQ8sHQWD17eDEseDIuLi54bX26zVk9e3kxLHkyLi4ueW591+7OqsrkyOujrMrutNnBvbj2yv3X6WO6zWKjrMbk1tBjW2ldW2pdtOa0olhpus1ZarXE1+6zpLmrubLX09DywdC1xLXEs6S2yKOsYltpXVtqXbzHwrxjW2ldW2pdtcQmIzIwNTQwO8rH08nExNK7uPbX087KzOK94r72tcO1vbXEoaO+38zltPrC68q1z9bI58/Co6hweXRob26jqTwvcD4KPHByZSBjbGFzcz0="brush:java;">def LCSLength(m, n, x=None, y=None,c=None, b=None): for i in range(1, m): for j in range(1, n): if x[i-1] == y[j-1]: c[i][j] = c[i-1][j-1]+1 b[i][j] = 'equals' elif c[i-1][j] >= c[i][j-1]: c[i][j] = c[i-1][j] b[i][j] = 'up' else: c[i][j] = c[i][j-1] b[i][j] = 'left'

5 构造最长公共子序列

由算法LCSLength计算得到的数组b可用于快速构造序列X={x1,x2...xm}和Y={y1,y2...yn}的最长公共子序列。首先从b[m][n]开始,根据b[i][j]值所指向的方向进行搜索。equals表示等于,即X和Y的最长公共子序列为Xi-1和Yj-1的最长公共子序列加上xi所得的最长公共子序列,left为向左,即Xi和Yj的最长公共子序列与Xi和Yj的最长公共子序列相同,up表示向上,即Xi和Yj的最长公共子序列与Xi-1和Yj的最长公共子序列相同。下面LCS算法为具体的求解过程。

def GetLCS(i, j, x=None, b=None):
    if i == 0 and j == 0:
        return
    if b[i][j] == 'equals':
        GetLCS(i-1,j-1, x, b)
        print x[i-1], ' '
    elif b[i][j] == 'left':
        GetLCS(i, j-1, x, b)
    else:
        GetLCS(i-1, j, x, b)

6 实例分析

现有两个序列X={A,B,C,B,D,A,B}和Y={B,D,C,A,B,A},由算法LCSLength和LCS计算出的结果如图所示:

\

7 代码运行实现


#-*-coding:utf8-*-
"""

Author  : xianghonglee
Email   : xianghongleeking@gmail.com
Created on '14-5-26'
"""

def LCSLength(m, n, x=None, y=None,c=None, b=None):
    for i in range(1, m):
        for j in range(1, n):
            if x[i-1] == y[j-1]:
                c[i][j] = c[i-1][j-1]+1
                b[i][j] = 'equals'
            elif c[i-1][j] >= c[i][j-1]:
                c[i][j] = c[i-1][j]
                b[i][j] = 'up'
            else:
                c[i][j] = c[i][j-1]
                b[i][j] = 'left'

def GetLCS(i, j, x=None, b=None):
    if i == 0 and j == 0:
        return
    if b[i][j] == 'equals':
        GetLCS(i-1,j-1, x, b)
        print x[i-1], ' '
    elif b[i][j] == 'left':
        GetLCS(i, j-1, x, b)
    else:
        GetLCS(i-1, j, x, b)


if __name__ == '__main__':
    x ="ABCBDAB"
    y = "BDCABA"
    m = len(x)+1
    n = len(y)+1
    b = [[0 for i in range(n)] for j in range(m)]
    c = [[0 for i in range(n)] for j in range(m)]

    LCSLength(m,n,x,y,c,b)
    for i in c:
        print i
    for j in b:
        print j
    GetLCS(m-1,n-1,x,b)

打印结果为:



评论关闭