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中每个字符串的平均命中率大约是