找到所有子串的最快方法是什么?

时间:2009-10-12 20:20:11

标签: algorithm search

纯粹是出于好奇。我正在浏览一篇比较各种字符串搜索算法的文章,并注意到它们都是为了找到第一个匹配的子字符串而设计的。这让我想到......如果我想找到所有出现的子串怎么办?

我确信我可以创建一个使用KMP或BM变体的循环,并将每个找到的事件转储到一个数组中,但这似乎不是最快的。

分而治之的算法难道不是优越的吗?

例如,假设您在字符串“abbcacabbcabcacbccbabc”中查找序列“abc”。

  1. 在第一遍中查找第一个字符的所有匹配并存储其位置。
  2. 在每个额外的传球上使用前一传球的位置来查找下一个角色的所有出现次数,减少每次迭代的下一次传球的候选。
  3. 考虑到我提出这个想法的难易程度,我认为有人已经想出了它并在30年前对其进行了改进。

4 个答案:

答案 0 :(得分:11)

请参阅Suffix array

  

<强>应用

     

字符串的后缀数组可以是   用作快速定位的索引   每次出现一个子串   字符串。找到每一个事件   子串的等价于   找到以...开头的每个后缀   子串。感谢   字典排序,这些   后缀将组合在一起   后缀数组,可以找到   有效地使用二进制搜索。如果   这个直截了当地实现了   二分搜索需要O(mlogn)时间,   其中m是长度   子。避免重做   比较,额外的数据结构   提供最长的信息   后缀的公共前缀(LCP)是   构造,给出O(m + logn)搜索   时间。

答案 1 :(得分:3)

如果您只处理一次给定的字符串,则后缀数组是过度的。它需要O(n log n)时间来创建,因此KMP样式算法将击败它。此外,如果您的字符串很大,或者您希望在收到字符串时实时获得结果,则后缀数组将不起作用。

除了用于存储匹配的内存之外,当然可以修改KMP算法以在找到匹配后继续运行,而不是用于存储匹配的内存(如果您只是打印出匹配项,这可能也是不必要的)或者在你去的时候处理它们。首先,取Wikipedia implementation并修改“return m”语句以“将m添加到索引列表”。但你还没有完成。您还需要问问自己,您是否允许重叠事件?例如,如果您的子字符串是“abab”并且您正在查找主字符串“abababab”,是否有两次出现或三次出现?在我给出的例子中(“作为一个开始”),你可以将i重置为0以给出“两个”答案,或者你可以在“添加m”之后落入“其他”情况以给出“三个” “回答。

答案 2 :(得分:1)

没有单一的“最快方式”取决于

A)字符串实际上是什么(长度,字符分布......)

B)运行的硬件

C)如果您想要所有结果并行或顺序

D)其他参数(例如,找到的元素重叠,您要搜索一次还是多次)

E)如果您认为此实施具体或仅仅是学术性的。在实现中,有许多其他方法可以优化内容。例如。临时存储(如你的建议)通常非常昂贵。

你有的想法,例如将完全废弃任何CPU缓存长串。所以在那些情况下它会非常慢。

答案 3 :(得分:0)

KMP和BM都可以轻松用于查找多个匹配项。我还建议使用Rabin-Karp,其中恕我直言更容易理解,但多次匹配的速度并不快(O(n + k * m)我认为,其中n是文本的长度,m是长度模式和k是出现次数)。但是对于重叠和非重叠匹配都很容易修改。

它也可以使用后缀树/后缀数组来完成,但它们更难编码,并且不会真正为您带来任何速度提升。