最长公共子序列(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 这两个软件的文本比较据说都采用了这个算法。两者的速度可以相差几十倍。以后有空可以看一下。

浙公网安备 33010602011771号