问题描述
最快(&python)方式获取不包含任何其他元素作为其前缀的元素列表。
(元素可以是任何顺序,为了清楚起见,解释元素在这里保持顺序,所以如果需要排序必须明确地完成)
输入是
['AB', 'ABC', 'ABCDEF', 'ABCDEFG', 'BCD', 'DEF', 'DEFGHI', 'EF', 'GKL', 'JKLM']
元素消除:
'AB' prefix of 'ABC'
'ABC' prefix of 'ABCDEF'
'ABCDEF' prefix OF 'ABCDEFG'
'DEF' prefix of 'DEFGHI'
预期产出
['ABCDEFG', 'BCD', 'DEFGHI', 'EF', 'GKL', 'JKLM']
编辑 :
增加一点复杂性(或清晰度)。 列表的平均长度从500到900不等。
1楼
列表理解( ls
是输入列表的名称):
[x for x in ls if x not in [y[:len(x)] for y in ls if y != x]]
我怀疑它在性能方面是最快的,但这个想法非常简单。 您将逐个元素地遍历list,并检查它是否是所有其余元素列表中任何元素的前缀。
timeit结果:每循环11.9 us(尽管如果要将它用于大型列表,缩放更重要)
2楼
如果您的列表已排序,则每个元素都是下一个元素的前缀,或者不是任何元素的前缀。 因此,你可以写:
ls.sort()
[ls[i] for i in range(len(ls))[:-1] if ls[i] != ls[i+1][:len(ls[i])]] + [ls[-1]]
这将是n log(n)
排序加上一次遍历列表( n
)。
对于您当前的排序列表,它也稍微快一些,因为它是线性的,timeit给出2.11 us。
使用zip
可以稍微快一点(但不是渐近),也有更多的pythonic:
[x for x, y in zip(ls[:-1], ls[1:]) if x != y[:len(x)]] + [ls[-1]]
timeit给出1.77我们
3楼
如果您的列表最初是无序的,则首先执行ls.sort()
。
使用startswith
:
In [71]: [i for i, j in zip(ls[:-1], ls[1:]) if not j.startswith(i)]+[ls[-1]]
Out[71]: ['ABCDEFG', 'BCD', 'DEFGHI', 'EF', 'GKL', 'JKLM']
或enumerate
:
[v for i, v in enumerate(ls[:-1]) if not ls[i+1].startswith(v)]+[ls[-1]]
与@ sashkello的方法相比:
In [78]: timeit [v for i, v in enumerate(ls[:-1]) if not ls[i+1].startswith(v)]+[ls[-1]]
10000 loops, best of 3: 29.6 us per loop
In [79]: timeit [i for i, j in zip(ls[:-1], ls[1:]) if not j.startswith(i)]+[ls[-1]]
10000 loops, best of 3: 28.5 us per loop
In [80]: timeit [x for x in ls if x not in [y[:len(x)] for y in ls if y != x]]
1000 loops, best of 3: 1.77 ms per loop