20

Given:列表的列表,例如[[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]

Todo:查找所有子列表中最长的公共前缀。

Exists:在另一个线程“两个列表之间的公共元素不使用 Python 中的集合”中,建议使用“计数器”,它在 python 2.7 之上可用。但是我们当前的项目是用 python 2.6 编写的,所以没有使用“计数器”。

我目前这样编码:

l = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
newl = l[0]
if len(l)>1:
    for li in l[1:]:
    newl = [x for x in newl if x in li]

但我发现它不是很pythonic,有没有更好的编码方式?

谢谢!

新编辑:抱歉提一下:在我的情况下,“l”中列表的共享元素具有相同的顺序,并且总是从第 0 项开始。所以你不会有像[[1,2,5,6],[2,1,7]]

4

6 回答 6

57

os.path.commonprefix()适用于列表:)

>>> x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> import os
>>> os.path.commonprefix(x)
[3, 2, 1]
于 2014-01-28T23:31:38.230 回答
18

我不确定它是多么pythonic

from itertools import takewhile,izip

x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]

def allsame(x):
    return len(set(x)) == 1

r = [i[0] for i in takewhile(allsame ,izip(*x))]
于 2012-06-29T14:41:24.823 回答
2

这是使用 itertools 的另一种方法:

>>> import itertools
>>> L = [[3,2,1,4], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> common_prefix = []
>>> for i in itertools.izip(*L):
...    if i.count(i[0]) == len(i):
...       common_prefix.append(i[0])
...    else:
...       break
... 
>>> common_prefix
[3, 2, 1]

不确定它可能被认为是多么“pythonic”。

于 2012-06-29T14:36:27.093 回答
2

鉴于您的示例代码,您似乎想要一个reduce(set.intersection, map(set, l))保留第一个列表的初始顺序的版本。

这需要算法改进,而不是风格改进;单独的“pythonic”代码在这里对你没有任何好处。想一想每个列表中出现的所有值都必须满足的情况:

给定一个列表列表,当且仅当它出现在nlist列表中时,一个值才会出现在每个列表中,其中nlist是列表的总数。

如果我们可以保证每个值在每个列表中只出现一次,那么上面的内容可以改写为:

给定一个唯一项目列表的列表,当且仅当它出现的nlist次数总计时,每个列表中才会出现一个值。

我们可以使用集合来保证列表中的项目是唯一的,因此我们可以将后一个原则与一个简单的计数策略结合起来:

>>> l = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> count = {}
>>> for i in itertools.chain.from_iterable(map(set, l)):
...     count[i] = count.get(i, 0) + 1
...     

现在我们要做的就是过滤原始列表:

>>> [i for i in l[0] if count[i] == len(l)]
[3, 2, 1]
于 2012-06-29T14:22:32.880 回答
0

它效率低下,因为它不会在发现不匹配后立即退出,但它很整洁:

([i for i,(j,k) in enumerate(zip(a,b)) if j!=k] or [0])[0]
于 2014-09-19T18:46:33.453 回答
0

使用生成器表达式和 Python 3 内置的现代化垂直扫描zip解决方案:

lst = [[3,2,1], [3,2,1,1,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]

next(zip(*(x for x in zip(*lst) if len(set(x)) == 1)))
# (3, 2, 1)

另请参阅相关的Leetcode 问题 - Longest Common Prefix

于 2018-08-16T23:37:07.137 回答