打印最长的公共序列

时间:2014-09-27 16:20:13

标签: algorithm python data-structures

我正在尝试实现经典问题的解决方案,但我感到很困惑,因为我遇到了一些我找不到好结果的情况。

输出:“最长公共序列”,“长度”

def longestCommonSeq(x,y):
    LCS = [[0 for z in range(len(y)+1)] for z in  range(len(x)+1)] 
    for i in range(len(x)):
        LCS[i][0]=0

    for j in range(len(y)):
        LCS[0][j]=0

    for i in range(len(x)):
        for j in range(len(y)):
            if x[i]== y[j]:
                LCS[i][j]= 1+LCS[i-1][j-1]
            else: 
                LCS[i][j]= max(LCS[i-1][j],LCS[i][j-1])


    i, j= len(x),len(y)
    print LCS

    res = "" 
    while i>0 and j>0:

        if x[i-1]== y[j-1]:
            res= x[i-1] + res
            i-=1
            j-=1 
        else:
            if LCS[i-1][j]>LCS[i][j-1]:
                i-=1
            else: 
                j-=1

    return res, LCS[len(x)-1][len(y)-1]

对于第一种情况:

x= "AGGTAB"
y= "GXTXAYB"
print longestCommonSeq(x,y)

结果: ('GTAB', 4) 这是正确的!

但是对于这样的情况:

a= "APBCADCQER" 
b= "RASBTAUCVE"
print longestCommonSeq(a,b)

 ('', 5)

我错过了什么?,任何人都可以提示吗?

1 个答案:

答案 0 :(得分:2)

你正在建造错误的桌子;您应该将行i = 0j = 0处的行完全保留为0,并将匹配放入较高的行。这意味着您需要在第一个循环中将ij坐标添加1,因为Python将索引设置为0。

结果,你最终得到一张最后有0的表;使用与explanation on Wikipedia类似的符号来构建它:

┌─┬─────┬─────────┬─────────┬─────┬──────┬───────┬─┐
│ │A    │G        │G        │T    │A     │B      │ │
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│G│< ^ 0│\ G      │\ G      │< G  │< G   │< G    │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│X│< ^ 0│^ G      │< ^ G    │< ^ G│< ^ G │< ^ G  │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│T│< ^ 0│^ G      │< ^ G    │\ GT │< GT  │< GT   │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│X│< ^ 0│^ G      │< ^ G    │^ GT │< ^ GT│< ^ GT │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│A│\ A  │< ^ A | G│< ^ A | G│^ GT │\ GTA │< GTA  │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│I│^ A  │< ^ A | G│< ^ A | G│^ GT │^ GTA │< ^ GTA│0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│B│^ A  │< ^ A | G│< ^ A | G│^ GT │^ GTA │\ GTAB │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│ │0    │0        │0        │0    │0     │0      │0│
└─┴─────┴─────────┴─────────┴─────┴──────┴───────┴─┘

< and ^: maximum picked
\: add matching character to cell to the top left.

这会将您的上一列和行设置为0而不是第一列。幸运的是,Python将-1解释为最后一个元素,因为在i = 0j = 0这正是您最终要做的事情。你想要的是:

┌─┬─┬─────┬─────────┬─────────┬─────┬──────┬───────┐
│ │ │A    │G        │G        │T    │A     │B      │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│ │0│0    │0        │0        │0    │0     │0      │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│G│0│< ^ 0│\ G      │\ G      │< G  │< G   │< G    │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│X│0│< ^ 0│^ G      │< ^ G    │< ^ G│< ^ G │< ^ G  │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│T│0│< ^ 0│^ G      │< ^ G    │\ GT │< GT  │< GT   │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│X│0│< ^ 0│^ G      │< ^ G    │^ GT │< ^ GT│< ^ GT │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│A│0│\ A  │< ^ A | G│< ^ A | G│^ GT │\ GTA │< GTA  │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│I│0│^ A  │< ^ A | G│< ^ A | G│^ GT │^ GTA │< ^ GTA│
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│B│0│^ A  │< ^ A | G│< ^ A | G│^ GT │^ GTA │\ GTAB │
└─┴─┴─────┴─────────┴─────────┴─────┴──────┴───────┘

接下来,您不需要在第二个循环中咨询xy,您只需 LCS表。由于您最后得到0,因此在最后一行和第一列中,当xy的最后一个字符不匹配时,您的算法版本会停滞不前由于未能达到表中列出的LCS路径,因此几乎随机递减ij

请注意,前两个for循环完全是冗余的,可以删除;您的整个LCS表仅包含0个值,为什么还要将它们设置为0?维基百科中的伪代码使用从1到长度(包括)的循环,但是由于Python从0开始并且你在长度上加1以保持包含长度,所以你不需要在这里执行此操作。

您的更正后的功能如下:

def longestCommonSeq(x,y):
    LCS = [[0 for z in range(len(y) + 1)] for z in range(len(x) + 1)] 
    for i in range(len(x)):
        for j in range(len(y)):
            if x[i] ==  y[j]:
                LCS[i + 1][j + 1] = 1 + LCS[i][j]
            else: 
                LCS[i + 1][j + 1] = max(LCS[i][j + 1], LCS[i + 1][j])

    res = ''
    i, j = len(x), len(y)

    while i and j:
        if LCS[i][j] == LCS[i-1][j]:
            i -= 1
        elif LCS[i][j] == LCS[i][j-1]: 
            j -= 1
        else:
            res = x[i-1] + res
            i -= 1
            j -= 1

    return res, LCS[-1][-1]

由于LCS表现已正确构建,因此我们也可以使用LCS[-1][-1]作为最大长度值。

这会产生预期的结果:

>>> x = "AGGTAB"
>>> y = "GXTXAYB"
>>> print longestCommonSeq(x, y)
('GTAB', 4)
>>> a = "APBCADCQER" 
>>> b = "RASBTAUCVE"
>>> print longestCommonSeq(a, b)
('ABACE', 5)

另一种方法是在第二阶段修复一个一个,并查看实际对应输入字的LCS表格单元格;例如从ij减去1:

def longestCommonSeq(x,y):
    LCS = [[0 for z in range(len(y)+1)] for z in  range(len(x)+1)] 

    for i in range(len(x)):
        for j in range(len(y)):
            if x[i] ==  y[j]:
                LCS[i][j] = 1 + LCS[i - 1][j - 1]
            else: 
                LCS[i][j] = max(LCS[i - 1][j], LCS[i][j - 1])

    i, j = len(x) - 1, len(y) - 1
    print LCS

    res = "" 
    while i >= 0 and j >= 0:
        if x[i] == y[j]:
            res= x[i] + res
            i -= 1
            j -= 1 
        else:
            if LCS[i - 1][j] > LCS[i][j - 1]:
                i -= 1
            else: 
                j -= 1

    return res, LCS[len(x) - 1][len(y) - 1]

现在ij的范围分别从len(x) - 10len(y) - 10,您再次引用正确的单元格