纯粹是出于好奇。我正在浏览一篇比较各种字符串搜索算法的文章,并注意到它们都是为了找到第一个匹配的子字符串而设计的。这让我想到......如果我想找到所有出现的子串怎么办?
我确信我可以创建一个使用KMP或BM变体的循环,并将每个找到的事件转储到一个数组中,但这似乎不是最快的。
分而治之的算法难道不是优越的吗?
例如,假设您在字符串“abbcacabbcabcacbccbabc”中查找序列“abc”。
考虑到我提出这个想法的难易程度,我认为有人已经想出了它并在30年前对其进行了改进。
答案 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是出现次数)。但是对于重叠和非重叠匹配都很容易修改。
它也可以使用后缀树/后缀数组来完成,但它们更难编码,并且不会真正为您带来任何速度提升。