2014-10-04 101 views
1

我发现了很多查找列表交集的方法,但我在查找订单时找到找到交集的有效方法时遇到了问题。查找两个列表的重叠,保留序列的顺序

list1 = [1, 2, 3, 4, 5, 6, 7] 
list2 = [7, 6, 3, 4, 5, 8] 

函数应该返回[3, 4, 5]

我已经知道了,只有一个重叠的顺序,我就知道它的最小长度,而不是它的确切长度。

+0

听起来像[最长的公共子序列](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem)问题给我。 – 2014-10-04 21:36:30

+0

@MartijnPieters,你是对的,现在我知道该怎么称呼它了,我发现了一些算法。 – prooffreader 2014-10-04 21:39:28

+0

嗯...我希望通过知道只有一个共同的子序列并且具有最小长度,我可以避免使用O(MN)算法。 – prooffreader 2014-10-04 22:26:00

回答

3

您正在寻找Longest Common Subsequence算法;以下使用动态编程来发现在O(NM)中的元素的时间(长度为N和M序列):

def lcs(a, b): 
    tbl = [[0 for _ in range(len(b) + 1)] for _ in range(len(a) + 1)] 
    for i, x in enumerate(a): 
     for j, y in enumerate(b): 
      tbl[i + 1][j + 1] = tbl[i][j] + 1 if x == y else max(
       tbl[i + 1][j], tbl[i][j + 1]) 
    res = [] 
    i, j = len(a), len(b) 
    while i and j: 
     if tbl[i][j] == tbl[i - 1][j]: 
      i -= 1 
     elif tbl[i][j] == tbl[i][j - 1]: 
      j -= 1 
     else: 
      res.append(a[i - 1]) 
      i -= 1 
      j -= 1 
    return res[::-1] 

演示:

>>> def lcs(a, b): 
...  tbl = [[0 for _ in range(len(b) + 1)] for _ in range(len(a) + 1)] 
...  for i, x in enumerate(a): 
...   for j, y in enumerate(b): 
...    tbl[i + 1][j + 1] = tbl[i][j] + 1 if x == y else max(
...     tbl[i + 1][j], tbl[i][j + 1]) 
...  res = [] 
...  i, j = len(a), len(b) 
...  while i and j: 
...   if tbl[i][j] == tbl[i - 1][j]: 
...    i -= 1 
...   elif tbl[i][j] == tbl[i][j - 1]: 
...    j -= 1 
...   else: 
...    res.append(a[i - 1]) 
...    i -= 1 
...    j -= 1 
...  return res[::-1] 
... 
>>> list1 = [1, 2, 3, 4, 5, 6, 7] 
>>> list2 = [7, 6, 3, 4, 5, 8] 
>>> lcs(list1, list2) 
[3, 4, 5] 

这将找到的子序列位置无关的和如果其他元素混在其中:

>>> lcs([1, 2, 3, 4, 5, 6, 7], [7, 3, 6, 4, 8, 5]) 
[3, 4, 5] 
+0

如果我已经知道子序列的最小长度,并且只有一个,有什么办法比O(MN)算法做得更好? – prooffreader 2014-10-04 22:26:44

+0

@prooffreader:你仍然必须找到这些元素,这意味着你必须扫描这两个序列。 – 2014-10-04 22:31:22

+0

@prooffreader:这里的最小长度并不能真正帮到你。知道*最大*长度会让你在第一阶段提前停止搜索,并提前一点移动到第二阶段(重建子序列)。 – 2014-10-04 23:43:18