【问题标题】:Longest common sequence between many sub-sequences许多子序列之间的最长公共序列
【发布时间】:2017-11-02 18:53:00
【问题描述】:

花哨的标题:) 我有一个包含以下内容的文件:

>sequence_40
ABCDABDCABCDBACDBACDBACDBACDABDCDC
ACDCCDCABDCADCADBCACBDCABD
>sequence_41
DCBACDBACDADCDCDCABCDCACBDCBDACBDC
BCDBABABBABACDCDBCACDBACDBACDBACDC
BCDB
...

然后,我有一个函数,它返回一个字典(称为 dict),它将序列作为键返回,将字符串(组合在一行上)作为键的值返回。序列范围从 40 到 59。 我想获取一个序列字典并返回在所有序列中找到的最长的公共子序列。设法在stackoverflow上找到了一些帮助,并制作了一个只比较该字典中最后两个字符串的代码,而不是全部:)。 这是代码

def longest_common_sequence(s1, s2):
    m = [[0] * (1 + len(s2)) for i in range(1 + len(s1))]
    longest, x_longest = 0, 0
    for x in range(1, 1 + len(s1)):
        for y in range(1, 1 + len(s2)):
            if s1[x - 1] == s2[y - 1]:
                m[x][y] = m[x - 1][y - 1] + 1
                if m[x][y] > longest:
                    longest = m[x][y]
                    x_longest = x
            else:
                m[x][y] = 0
    return s1[x_longest - longest: x_longest]

for i in range(40,59):
    s1=str(dictionar['sequence_'+str(i)])
    s2=str(dictionar['sequence_'+str(i+1)])
longest_common_sequence(s1,s2)

如何修改它以获取字典中所有序列之间的公共子序列?谢谢!

【问题讨论】:

  • 首先不要使用dict作为变量名;它掩盖了dict 类型/构造函数,并且很快就会咬你。
  • 现在改了 :) 谢谢!

标签: python dictionary sequence


【解决方案1】:

编辑:正如@lmcarreiro 指出的那样,substrings(或 subarrayssublists)和 subsequences 之间存在相关差异。据我了解,我们在这里都在谈论子字符串,所以我将在我的回答中使用这个术语。

Guillaumes 的答案可以改进:

def eachPossibleSubstring(string):
  for size in range(len(string) + 1, 0, -1):
    for start in range(len(string) - size + 1):
      yield string[start:start+size]

def findLongestCommonSubstring(strings):
  shortestString = min(strings, key=len)
  for substring in eachPossibleSubstring(shortestString):
    if all(substring in string
        for string in strings if string != shortestString):
      return substring

print findLongestCommonSubstring([
  'ABCDABDCABCDBACDBACDBACDBACDABDCDCACDCCDCABDCADCADBCACBDCABD',
  'DCBACDBACDADCDCDCABCDCACBDCBDACBDCBCDBABABBABACDCDBCACDBACDBACDBACDCBCDB',
])

打印出来:

ACDBACDBACDBACD

这更快,因为我返回第一个找到并从最长到最短搜索。

基本思想是这样的:获取最短字符串的每个可能子字符串(按从最长到最短的顺序),看看是否可以在所有其他字符串中找到该子字符串。如果是,则返回它,否则尝试下一个子字符串。

您需要了解生成器。试试 e。 G。这个:

for substring in eachPossibleSubstring('abcd'):
  print substring

print list(eachPossibleSubstring('abcd'))

【讨论】:

  • 哈!这需要我一段时间才能理解:D
  • 顺便说一句,我感觉这个问题可以有更快的解决方案,类似于Boyer-Moore-Horspool algorithm
  • 这是子串还是子序列的解决方案?例如,看起来您正在使用子字符串,而不是子序列......
  • 来自维基百科:“与子串不同,子序列不需要占据原始序列中的连续位置”
  • 是的,这个问题确实是关于子字符串的......我只是指出了这一点,因为我正在为最长公共子序列寻找一个好的解决方案(按照它在 WP 中定义的方式)
【解决方案2】:

我首先定义一个函数来返回给定序列的所有可能子序列:

from itertools import combinations_with_replacement
def subsequences(sequence):
    "returns all possible subquences of a given sequence"
    for start, stop in combinations_with_replacement(range(len(sequence)), 2):
        if start < stop:
            yield sequence[start:stop]

然后我会用另一种方法来检查给定的子序列是否存在于所有给定的序列中:

def is_common_subsequence(sub, sequences):
    "returns True if <sub> is a common subsequence in all <sequences>"
    return all(sub in sequence for sequence in sequences)

然后使用上面的 2 种方法很容易获得给定序列集中的所有常见子序列:

def common_sequences(sequences):
    "return all subsequences common in sequences"
    shortest_seq = min(sequences, key=len)
    return set(subsequence for subsequence in subsequences(shortest_seq) \
       if is_common_subsequence(subsequence, sequences))

...并提取最长序列:

def longuest_common_subsequence(sequences):
    "returns the longuest subsequence in sequences"
    return max(common_sequences(sequences), key=len)

结果:

sequences = {
    41: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    42: '123ABCDEFGHIJKLMNOPQRSTUVW',
    43: '123456ABCDEFGHIJKLMNOPQRST'
}

sequences2 = {
    0: 'ABCDEFGHIJ',
    1: 'DHSABCDFKDDSA',
    2: 'SGABCEIDEFJRNF'
}

print(longuest_common_subsequence(sequences.values()))
>>> ABCDEFGHIJKLMNOPQRST

print(longuest_common_subsequence(sequences2.values()))
>>> ABC

【讨论】:

  • 整洁。但是您可以优化检查从最长到最短的可能序列并返回第一个找到的而不是收集所有然后返回最大值。
  • 嘿!这就是我一直在寻找的。里面的所有功能都很好。你能告诉我这里的这个人是什么吗:combinations_with_replacement?
  • 糟糕,这是我忘记复制/粘贴的导入。固定
  • @Alfe:只取最短输入序列的子序列确实是个好主意。我会更新我的答案
  • 演示是这里的主要目标,而不是优化;)
【解决方案3】:

在这里你有一个可能的方法。首先让我们定义一个返回两个字符串之间最长子字符串的函数:

def longest_substring(s1, s2):
    t = [[0]*(1+len(s2)) for i in range(1+len(s1))]
    l, xl = 0, 0
    for x in range(1,1+len(s1)):
        for y in range(1,1+len(s2)):
            if s1[x-1] == s2[y-1]:
                t[x][y] = t[x-1][y-1] + 1
                if t[x][y]>l:
                    l = t[x][y]
                    xl  = x
            else:
                t[x][y] = 0
    return s1[xl-l: xl]

现在我将为示例创建一个随机的dict 序列:

import random
import string

d = {i : ''.join(random.choice(string.ascii_uppercase) for _ in range(50)) for i in range(10)}

print d

{0: 'ASCUCEVJNIGWVMWMBBQQBZYBBNGQAJRYXACGFEIFWHMBCNYRGL', 1: 'HKUKZOJJUCRTSBLNZXCIBARLPNAPAABRBZEVGVILJAFCGWGQVV', 2: 'MMHCYPKECRJFEWTGYITMHZSNHAFEZVFYDAVILRYRKIDDBEFRVX', 3: 'DGBULRFJINFZEELDASRFBIRSADWMRAYMGCDAOJDKQIMXIRLTEI', 4: 'VDUFWZSXLRGOIMAHOAMZAIWDPTHDVDXUACRBASJMCUHREDORRH', 5: 'RFGAVHOWNKRZMYMSFSSNUGCKEWUNVETCDWJXSPBJHKSTPFNSJO', 6: 'HFMLMHCFSOEXBXWFAROIRGJNPRTKRWCEPLFOKGMXNUPCPWREWX', 7: 'CNPGSHGVIRLDXAADXUVWCTJCXUHQLALBUOJMXQBKXWHKGSJHEH', 8: 'UWDXXTRCFNCBUBEYGYTDWTPLNTRHYQWKTHPRVCBAWIMNGHULDC', 9: 'OOCJRXBZKJIGHZEJOOIKWKMQKIEQVPEDTFPJQAUQKJQVLOMGJB'}

最后,我们需要找到所有序列之间最长的子序列:

import itertools
max([longest_substring(i,j) for i,j in itertools.combinations(d.values(), 2)], key=len)

输出:

'VIL'    

【讨论】:

  • 感谢您的回答!我似乎不明白的一件事是,您的代码输出“VIL”确实显示在键 1 和 2 中,但不在其余部分中:)。我对子字符串感兴趣,而不是单独的字母。示例:0:ABCDEFGHIJ 1:DHSABCDFKDDSA 2:SGABCEIDEFJRNF 输出应该是 'ABC',因为它是上述所有序列中最长的公共字符串。
猜你喜欢
  • 2011-08-10
  • 1970-01-01
  • 2011-03-01
  • 2013-12-06
  • 2011-02-25
  • 2013-02-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多