![](/img/trans.png)
[英]Pythonic way of filtering out a list of strings from another list of strings
[英]Is there a Pythonic way of filtering substrings of strings in a list?
我有一个包含如下字符串的列表。
candidates = ["Hello", "World", "HelloWorld", "Foo", "bar", "ar"]
我希望列表被过滤为["HelloWorld", "Foo", "Bar"]
,因为其他的都是子字符串。 我可以这样做,但不要认为它很快或优雅。
def filter_not_substring(candidates):
survive = []
for a in candidates:
for b in candidates:
if a == b:
continue
if a in b:
break
else:
survive.append(a)
return survive
有什么快速的方法吗?
怎么样:
candidates = ["Hello", "World", "HelloWorld", "Foo", "bar", "ar"]
result = [c for c in candidates if not any(c in o and len(o) > len(c) for o in candidates)]
print(result)
与评论中的建议相反:
from timeit import timeit
def filter_not_substring(candidates):
survive = []
for a in candidates:
for b in candidates:
if a == b:
continue
if a in b:
break
else:
survive.append(a)
return survive
def filter_not_substring2a(candidates):
return [c for c in candidates if not any(len(o) > len(c) and c in o for o in candidates)]
def filter_not_substring2b(candidates):
return [c for c in candidates if not any(c in o and len(o) > len(c) for o in candidates)]
xs = ["Hello", "World", "HelloWorld", "Foo", "bar", "ar", "bar"]
print(filter_not_substring(xs), filter_not_substring2a(xs), filter_not_substring2b(xs))
print(timeit(lambda: filter_not_substring(xs)))
print(timeit(lambda: filter_not_substring2a(xs)))
print(timeit(lambda: filter_not_substring2b(xs)))
结果:
['HelloWorld', 'Foo', 'bar', 'bar'] ['HelloWorld', 'Foo', 'bar', 'bar'] ['HelloWorld', 'Foo', 'bar', 'bar']
1.5163685
4.6516653
3.8334089999999996
因此,OP 的解决方案要快得多,但filter_not_substring2b
仍然比2a
快约 20%。 因此,首先进行len
比较并不能节省时间。
对于任何生产场景,OP 的 function 可能是最佳的 - 加速它的一种方法可能是将整个问题带入 C,但我怀疑这会显示出巨大的收益,因为逻辑已经非常简单了,我希望 ZA782173 到 ZA74217F32341B56B6也做得相当好。
用户@ming 指出,OP 的解决方案可以改进一点:
def filter_not_substring_b(candidates):
survive = []
for a in candidates:
for b in candidates:
if a in b and a != b:
break
else:
survive.append(a)
return survive
这个版本的 function 有点快,对我来说大约 10-15%
就渐近性能而言,我不相信O(n^2)
算法会更快(其中n == len(candidates)
,并假设字符串很短/长度受常数限制)。
如果有一个字符串列表,其中没有字符串是列表中任何其他元素的 substring,您需要检查此属性是否适用于列表中的每对字符串。 您需要检查O(n^2)
对这样的对,因此您的算法可能花费的最快时间是O(n^2)
时间。
Grismar 使用列表理解给出了答案,该列表理解在执行比较之前检查字符串的长度。 虽然这在实践中可能更快,但它并没有改善渐近运行时间,因为这样的改进(可能)是不可能的。
你可能会错过一个边缘案例。 考虑这种情况["a", "a"]
。 两个a
都是列表中另一个元素的 substring,因此两者都不应该存在。 但是,如果您知道字符串都是不同的,那就没有实际意义了。
如果您要求从算法的角度来看,我建议您使用trie data structure 。
算法草图:您将列表中的字符串一一插入其中。 例如候选人 = ["Hello","Hello World"]。 传统上,这些单词会存储为 'H'->'e'->'l'->'l'->'o' [word_flag]->->'W'->'o'->'r' ->'l'->'d'[word_flag]->无。 这里 word_flag 表示从根到该点的路径表示列表中的一个单词。 您唯一需要做的就是在遍历路径时只报告下一个元素为 None 的字符串。
复杂性:复杂性将是 O(sum([len(i) for i in Candidates])) 并且我相信这种复杂性应该非常接近最优,因为通常尝试是最高效的 memory 并且执行速度甚至超过hash 表在最坏的情况下。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.