查找多个字符串匹配的算法

Posted

技术标签:

【中文标题】查找多个字符串匹配的算法【英文标题】:Algorithm to find multiple string matches 【发布时间】:2011-03-16 17:08:34 【问题描述】:

我正在寻找有关在大量文本中查找所有匹配项的有效算法的建议。要搜索的术语将包含在一个列表中,并且可以有 1000 多种可能性。搜索词可能是 1 个或多个词。

显然,我可以多次通过文本与每个搜索词进行比较。效率不高。

我考虑过对搜索词进行排序并组合常见的子细分。这样我就可以快速消除大量的术语。语言是 C++,我可以使用 boost。

搜索词的示例可以是财富 500 强公司名称的列表。

想法?

【问题讨论】:

你到底想要什么?迭代所有匹配,匹配列表(即值),仅知道文本中至少有一个术语,...? 我需要知道文档中是否存在哪些搜索词 【参考方案1】:

您正在寻找的搜索词是单词还是完整的句子

如果只是单词,那么我建议从所有单词中构建一个Red-Black Tree,然后在树中搜索每个单词。

如果它可以是句子,那么它可能会变得更复杂......(?)

【讨论】:

无需自己构建红黑树。 std::multimap 就足够了。【参考方案2】:

所以你有很多搜索词,想看看文档中是否有它们?

如果正则表达式引擎将查看/ant|ape/ 并正确短路“ape”中的 a如果它没有在“蚂蚁”中找到它。如果没有,您可以对正则表达式进行“预编译”并将结果“压缩”到它们的最小重叠。 IE。在上述情况下,/a(nt|pe)/ 等等,对每个字母递归。

但是,执行上述操作非常类似于将所有搜索字符串放入 26 叉树(26 个字符,如果还有数字则更多)。将字符串推到树上,每个字符长度使用一个深度级别。

如果您的搜索字词数量很大,您可以使用您的搜索字词来实现超快的“此字词是否与我的搜索字词列表中的任何内容匹配”。

理论上你也可以反过来——将你的文档打包到树中,然后在上面使用搜索词——如果你的文档是静态的并且搜索词变化很大。

取决于您需要多少优化...

【讨论】:

【参考方案3】:

假设大量文本是静态英文文本并且您需要匹配整个单词,您可以尝试以下操作(您应该真正明确什么是“匹配”,您正在查看什么样的文本等问题)。

首先将整个文档预处理为Trie 或DAWG。

Trie/Dawg 具有以下属性:

给定一个 trie/dawg 和一个长度为 K 的搜索词,您可以在 O(K) 时间内查找与该词关联的数据(或判断是否不匹配)。

与 trie 相比,使用 DAWG 可以为您节省更多空间。尝试利用许多单词将具有共同前缀这一事实,而 DAWG 则利用共同前缀和共同后缀属性。

在 trie 中,还要准确地维护单词的位置列表。例如,如果文本是

That is that and so it is.

that 中最后一个 t 的节点将具有列表 1,3,is 中 s 的节点将具有关联的列表 2,7。

现在,当您获得一个单词搜索词时,您可以轻松地尝试并获得该词的匹配列表。

如果您得到一个多字搜索词,您可以执行以下操作。

使用搜索词中的第一个单词进行遍历。获取匹配列表并插入哈希表 H1。

现在用搜索词中的第二个词试一试。获取匹配列表。对于每个匹配位置 x,检查哈希表 H1 中是否存在 x-1。如果是这样,请将 x 添加到新的哈希表 H2。

使用第三个单词遍历 trie,获取匹配列表。对于每个匹配位置 y,检查 H3 中是否存在 y-1,如果存在则添加到新的哈希表 H3。

以此类推。

最后你会得到一个搜索短语的匹配列表,它给出了短语最后一个单词的位置。

您可以通过维护列表中位置的排序列表并进行二分搜索来优化短语匹配步骤:即例如。对于 H2 中的每个键 k,您在搜索词 3 的排序列表中对 k+1 进行二进制搜索,如果找到,则将 k+1 添加到 H3 等等。

【讨论】:

【参考方案4】:

不要重新发明***

这个问题已被深入研究。奇怪的是,搜索 ONE 模式/字符串的最佳算法并不容易外推到多字符串匹配。

"grep" 系列以非常有效的方式实现了多字符串搜索。如果您可以将它们用作外部程序,那就去做吧。

如果你真的需要实现算法,我认为最快的方法是重现 agrep 所做的事情(agrep 擅长多字符串匹配!)。 Here 是源文件和可执行文件。

而here 你会发现一篇论文描述了所使用的算法、理论背景以及关于字符串匹配的大量信息和指针。

需要注意的是:多字符串匹配已经被 Knuth、Boyer、Moore、Baeza-Yates 等人进行了大量研究。如果您需要一个真正快速的算法,请不要犹豫站在他们宽阔的肩膀上。不要重新发明***。

【讨论】:

感谢您的回复。我问这个问题是为了避免重新发明我认为可能已经过充分研究的东西。我去看看agrep src。 @Dwight:如果“大量文本”是静态的,我建议创建一个 trie/dawg,我认为这会击败 agrep 的算法,后者更适合搜索词的情况是固定的,但要搜索的文本会有所不同。无论如何,既然您接受了这个答案,我想您的大量文本并不是真正静态的。我建议您修改您的问题以添加该信息。 @Moron OP 说“要搜索的术语将包含在一个列表中,并且可以有 1000 多种可能性。”所以我假设文本不是静态的,因为如果是静态的,并且您有 1500 个要搜索的词,您可以提前构建一个树并执行二分搜索。 @Belisarius:只是想为未来的读者说清楚:-)从德怀特对乔治在这个问题上的回应中,似乎文本(“文档”)是静态的,所以有混淆范围。 @Moron 也许我误解了条件。我认为我现在能做的最好的事情是同意这个答案只对“动态”文档有意义。【参考方案5】:

此问题的最佳解决方案是使用suffix tree(或suffix array)。它本质上是一个字符串的所有后缀的 trie。对于长度为O(N) 的文本,可以在O(N) 中构建。

那么所有k出现的长度为m的字符串都可以在O(m + k)中得到最佳回答。

后缀树也可用于有效地查找例如最长回文、最长公共子串、最长重复子串等

这是分析 DNA 字符串时使用的典型数据结构,其长度可能为数百万/数十亿个碱基。

另见

Wikipedia/Suffix tree 字符串、树和序列的算法:计算机科学和计算生物学(Dan Gusfield)。

【讨论】:

您的意思是长度为N的文本吗? @JAB: 是的,我可以说长度N 而不是O(N),但是NO(N),所以它仍然是正确的=)【参考方案6】:

与单一模式的情况一样,多模式匹配有多种算法,您必须找到最适合您目的的一种。论文A fast algorithm for multi-pattern searching (archived copy) 对其中的大多数进行了回顾,包括 Aho-Corasick(这是 Knuth-Morris-Pratt 算法的多模式版本,具有线性复杂度)和 Commentz-Walter(Boyer- Moore 和 Aho-Corasick),并介绍了一个新的,它使用 Boyer-Moore 的想法来完成匹配多个模式的任务。

那篇论文中没有提到的另一种基于散列的算法是Rabin-Karp algorithm,它比其他算法具有更大的最坏情况复杂度,但通过散列降低线性因子来补偿它。哪个更好最终取决于您的用例。如果您想选择最快的一个,您可能需要实现其中的几个并在您的应用程序中进行比较。

【讨论】:

论文链接失效。 感谢您的通知。我已将其替换为存档版本。

以上是关于查找多个字符串匹配的算法的主要内容,如果未能解决你的问题,请参考以下文章

字符串匹配算法的使用(未完待整理)

串串的模式匹配算法(子串查找)BF算法KMP算法

常用算法3 - 字符串查找/模式匹配算法(BF & KMP算法)

字符串查找与匹配算法

字符串查找与匹配之BM算法

算法——蛮力法之顺序查找和蛮力字符串匹配