最长公共子序列(LCS)

最长公共子序列和最长子串是不一样的,公共子序列是A,B中按顺序出现的相同元素,元素不要求连续,而子串是要求连续的。

1暴力,对于较短的那条字符串,他的每个元素选中,或者没有被选中,有2^n种子串的情况,再与较长的比较,需要O(M*2N)。

2动态规划,关于动态规划,可参考http://wenku.baidu.com/view/9494d24be45c3b3567ec8bf9.html

动态规划,减少重复运算,用一个矩阵记录最大子序列长度,一个矩阵记录方向flag。时间,空间复杂度都是O(n2)。

Python代码

def lcs(a, b):
    lena = len(a)
    lenb = len(b)
    c = [[0 for i in range(lenb + 1)] for j in range(lena + 1)]
    flag = [[0 for i in range(lenb + 1)] for j in range(lena + 1)]
    for i in range(lena):
        for j in range(lenb):
            if a[i] == b[j]:
                c[i + 1][j + 1] = c[i][j] + 1
                flag[i + 1][j + 1] = 'ok'
            elif c[i + 1][j] > c[i][j + 1]:
                c[i + 1][j + 1] = c[i + 1][j]
                flag[i + 1][j + 1] = 'left'
            else:
                c[i + 1][j + 1] = c[i][j + 1]
                flag[i + 1][j + 1] = 'up'
    return c, flag


def printLcs(flag, a, i, j):
    l = []
    while i!= 0 and j != 0:
        if flag[i][j] == 'ok':
            l.append(a[i-1])
            i -= 1
            j -= 1
        elif flag[i][j] == 'left':
                j -= 1
        else: i -= 1
    return ''.join(reversed(l))

只求最长子序列的话

方法一:

def lcs(x, y):
    if len(x) == 0 or len(y) == 0:
        return ''
    if x[-1] == y[-1]:
        return lcs(x[:-1], y[:-1]) + x[-1]
    else:
        lcs1 = lcs(x,y[:-1])
        lcs2 = lcs(x[:-1],y)
        if len(lcs1) > len(lcs2):
            return lcs1
        else:
            return lcs2

方法二:

#调包
from itertools import combinations

def subsequences(s):
    """返回所有的子序列"""
    return set(''.join(c) for i in range(len(s) + 1) for c in combinations(s, i))

def lcs(x, y):
    """匹配"""
    return max(subsequences(x).intersection(subsequences(y)), key=len)

关于优化

不得不说动态规划算法还是效率太低。别人推荐一个比较好的算法, “An O(NP) Sequence Comparison Algorithm Sun Wu, Udi Manber, Gene Myers",subversion 和 git 这两个软件的文本比较据说都采用了这个算法。两者的速度可以相差几十倍。以后有空可以看一下。

posted @ 2016-11-08 11:45  夏末秋凉  阅读(315)  评论(0)    收藏  举报