序列列表之间最长的公共子序列

时间:2018-08-17 17:36:48

标签: algorithm sequence dynamic-programming

序列a和b的最长公共子序列(LCS)的长度可以在O(len(a)len(b))时间内由

计算
if a[i] != b[j]
    c[i, j] = max(c[i - 1][j], c[i][j - 1])
else
    c[i][j] = max(c[i - 1][j], c[i][j - 1], 1 + c[i - 1][j - 1])

其中c[i][j]a[0:i]b[0:j]的LCS的长度。您能以多快的速度计算xy之间的所有序列对的LCS,其中xy是序列列表?

我尝试直接计算每一对,这需要

Quadratic time in the length of either list

时间,它是xy的总序列长度的乘积。

1 个答案:

答案 0 :(得分:0)

首先,

您的重复关系是 错误

LCS(假设您自下而上)

if a[i] != b[j]
    c[i, j] = max(c[i - 1][j], c[i][j - 1]) # fine
else
    c[i][j] = 1 + c[i - 1][j - 1] # NO NEED FOR MAX HERE!! You can't have longer subsequence with a shorter string!

现在,要回答您的问题,如果您不使用动态编程(或者您曾经使用过并且遇到过最糟糕的情况,那就是没有重叠的子问题),那么您做不到比LCS x二次时间更好的了。

否则

您可以做的最优化是添加另一层动态编程。

假设采用自上而下的方法,请不要擦除包含2个子字符串之间的LCS的映射,这样您就可以在以后的计算中重用子问题

Python类似伪代码:

seq_lcs_map = map()

def computeLCS(str1, str2):
    if str1.empty() or str2.empty():
        return 0
    try:
        return seq_lcs_map[(str1,str2)]
    catch:
        try:
            seq_lcs_map[(str2,str1)]
        catch:
            if str1[0] == str2[0]:
                seq_lcs_map[(str1,str2)] = 1 + computeLCS(str1[1:], str2[1:])
            else:
                seq_lcs_map[(str1,str2)] = max(computeLCS(str1[1:],str2), computeLCS(str1,str2[1:]))
            seq_lcs_map[(str2, str1)] = seq_lcs_map[(str1, str2)]
            return seq_lcs_map[(str1, str2)]


def batch_LCS(l1, l2):
    for i in l1:
        for j in l2:
            try:
                seq_lcs_map[(i,j)]
            catch:
                try:
                    seq_lcs_map[(j,i)]
                catch:
                    seq_lcs_map[(i,j)] = seq_lcs_map[(j,i)] = computeLCS(i,j)

注意:映射键有点冗余,但是比起尝试获得最佳效果,更好地捕捉一些奇怪的边缘情况