Python 相互包含的有效字符串
我有两组字符串(Python 相互包含的有效字符串,python,regex,string,Python,Regex,String,我有两组字符串(A和B),我想知道A中的A和B中的B的所有字符串对,其中A是B的子字符串 对其进行编码的第一步如下: for a in A: for b in B: if a in b: print (a,b) 然而,我想知道——有没有更有效的方法来使用正则表达式实现这一点(例如,不要检查b:中的a是否为,而是检查regexp'.'.'+a+'.'.'.':是否与'b'匹配。我想,也许使用类似的方法可以让我缓存所有a的失败函数。此外,对b:循环中
A
和B
),我想知道A中的A和B
中的B的所有字符串对,其中A
是B
的子字符串
对其进行编码的第一步如下:
for a in A:
for b in B:
if a in b:
print (a,b)
然而,我想知道——有没有更有效的方法来使用正则表达式实现这一点(例如,不要检查b:
中的a是否为,而是检查regexp'.'.'+a+'.'.'.':
是否与'b'匹配。我想,也许使用类似的方法可以让我缓存所有a
的失败函数。此外,对b:
循环中的内部使用列表理解可能会带来相当大的加速(嵌套列表理解可能更好)
我对在算法的渐进运行时实现一个巨大的飞跃不太感兴趣(例如,使用后缀树或任何其他复杂和聪明的方法)。我更关心常量(我只需要对几对a
和B
集执行此操作,我不希望它运行一周)
你知道一些技巧吗?或者有什么通用的建议可以让你更快地做到这一点吗?非常感谢你能分享的任何见解
编辑:
根据@ninjagecko和@Sven Marnach的建议,我构建了一个包含10个mers的快速前缀表:
import collections
prefix_table = collections.defaultdict(set)
for k, b in enumerate(B):
for i in xrange(len(prot_seq)-10):
j = i+10+1
prefix_table[b[i:j]].add(k)
for a in A:
if len(a) >= 10:
for k in prefix_table[a[:10]]:
# check if a is in b
# (missing_edges is necessary, but not sufficient)
if a in B[k]:
print (a,b)
else:
for k in xrange(len(prots_and_seqs)):
# a is too small to use the table; check if
# a is in any b
if a in B[k]:
print (a, b)
当然,您可以轻松地将其作为一个列表:
[(a, b) for a in A for b in B if a in b]
这可能会稍微加快循环速度,但不要期望太多。我怀疑使用正则表达式在任何方面都会有所帮助
编辑:以下是一些计时:
import itertools
import timeit
import re
import collections
with open("/usr/share/dict/british-english") as f:
A = [s.strip() for s in itertools.islice(f, 28000, 30000)]
B = [s.strip() for s in itertools.islice(f, 23000, 25000)]
def f():
result = []
for a in A:
for b in B:
if a in b:
result.append((a, b))
return result
def g():
return [(a, b) for a in A for b in B if a in b]
def h():
res = [re.compile(re.escape(a)) for a in A]
return [(a, b) for a in res for b in B if a.search(b)]
def ninjagecko():
d = collections.defaultdict(set)
for k, b in enumerate(B):
for i, j in itertools.combinations(range(len(b) + 1), 2):
d[b[i:j]].add(k)
return [(a, B[k]) for a in A for k in d[a]]
print "Nested loop", timeit.repeat(f, number=1)
print "List comprehension", timeit.repeat(g, number=1)
print "Regular expressions", timeit.repeat(h, number=1)
print "ninjagecko", timeit.repeat(ninjagecko, number=1)
结果:
Nested loop [0.3641810417175293, 0.36279606819152832, 0.36295199394226074]
List comprehension [0.362030029296875, 0.36148500442504883, 0.36158299446105957]
Regular expressions [1.6498990058898926, 1.6494300365447998, 1.6480278968811035]
ninjagecko [0.06402897834777832, 0.063711881637573242, 0.06389307975769043]
编辑2:在计时中添加了一个变体。您可以看到它比所有暴力方法都要好得多
编辑3:使用集合而不是列表来消除重复项。(我没有更新计时——它们基本上保持不变。)让我们假设您的单词有一个合理的大小(比如10个字母)。执行以下操作以实现线性(!)时间复杂度,即O(a+B)
:
- 初始化哈希表或trie
- 对于b中的每个字符串b:
- 对于该字符串的每个子字符串
- 将子字符串添加到hashtable/trie(这不比
55*O(B)
=O(B)
)更糟糕),并使用它所属字符串的元数据
- 对于a中的每个字符串a:
- 对hashtable/trie执行
O(1)
查询,以查找它所在的所有B字符串,并生成这些字符串
(在撰写此答案时,如果OP的“单词”是有界的,则尚未得到响应。如果单词是无界的,则此解决方案仍然适用,但存在O(maxwordsize^2)
的依赖关系,尽管实际上这在实践中更好,因为并非所有单词的大小都相同,因此可能与O(averagewordsize^2)一样好)
具有正确的分布。例如,如果所有单词的大小都为20,则问题大小将比大小为10时增加4倍。但是如果从大小10->20增加足够少的单词,则复杂性不会有太大变化。)
编辑:实际上是一个理论上更好的答案。在这个答案发布之前,我正在查看链接的维基百科页面,并认为“字符串大小的线性不是你想要的”,直到后来才意识到这正是你想要的。你构建regexp的直觉(Aword1 | Aword2 | Aword3 |……)
是正确的,因为如果后台生成的有限自动机支持同时重叠匹配(并非所有regexp引擎都支持),它将快速执行匹配。最终,您应该使用什么取决于您是否计划重用As或Bs,或者这是否只是一次性的。上述技术更容易实现实现但仅在单词有界时有效(如果您不拒绝超过特定大小限制的单词,则会引入DoS漏洞),但如果您不想要或类似的,或者它无法作为库使用,则可能是您正在寻找的。为此,有专门的索引结构,请参阅示例
你可以为B构建一个后缀树或类似的东西,然后使用a来查询它。搜索大量字符串的一种非常快速的方法是使用一个有限自动机(因此你对regexp的猜测并不太清楚),也就是机器,它被用于grep、病毒扫描程序等工具中
首先,它将要搜索的字符串(在本例中为A中的单词)编译为具有失败函数的有限状态自动机(如果您对详细信息感兴趣,请参阅“from'75”)。该自动机然后读取输入字符串并输出所有找到的搜索字符串(可能您希望对其进行一点修改,以便它输出搜索字符串所在的字符串)
此方法的优点是它同时搜索所有搜索字符串,因此只需查看输入字符串的每个字符一次(线性复杂度)
有,但我还没有测试它们,所以我不能说这些实现的性能、可用性或正确性
编辑:我尝试了Aho Corasick自动机的实现,它确实是迄今为止建议的方法中速度最快的,而且也很容易使用:
import pyahocorasick
def aho(A, B):
t = pyahocorasick.Trie();
for a in A:
t.add_word(a, a)
t.make_automaton()
return [(s,b) for b in B for (i,res) in t.iter(b) for s in res]
不过,我观察到的一件事是,当使用@SvenMarnachs脚本测试这个实现时,它产生的结果比其他方法稍微少一些,我不知道为什么。我给创建者写了一封邮件,也许他会找到答案。你能大致说1.a
中有多少字符串,2.B中有多少字符串,3.如何A
中每个字符串的平均命中率是多少?len(A)
大约是70E3
,len(B)大约是10E3。A中每个字符串的平均命中率大约是