哪种字符串搜索算法实际上是最快的?


27

我一直被困在最快的字符串搜索算法上一段时间,听到了很多意见,但是最后我不确定。

我听到有人说最快的算法是Boyer-Moore,有人说Knuth-Morris-Pratt实际上更快。

我一直在寻找它们两者的复杂性,但是它们看起来大致相同O(n+m)。我发现在最坏的情况下,博耶·摩尔O(nm)要比具有O(m + 2 * n)的克努斯·莫里斯·普拉特复杂。其中,n =文本长度,m =模式长度。

据我所知,如果我使用加利尔定律,博耶-摩尔的情况将是最糟的。

我的问题,总的来说,这实际上是最快的字符串搜索算法(此问题包括所有可能的字符串算法,而不仅仅是Boyer-Moore和Knuth-Morris-Pratt)。

编辑:由于这个答案

我要寻找的是:

鉴于文本T和图形P我必须找到所有的出场PT

P和T的长度也来自[1,2 000 000],程序必须在0.15秒内运行。

我知道KMP和Rabin-Karp足以在该问题上获得100%的分数,但是我想尝试实施Boyer-Moore。哪种模式搜索最合适?


6
当用您选择的语言测试这些内容时,您发现了什么?
Walter

4
在某些测试中,Boyer-Moore在其他KMP上更好,但是我不确定我是否拥有它们的“最佳”实现。至于选择的语言,它在标签中:C ++(不确定,因为您编写了“选择的语言”,所以您是否看到了)。PS我也不确定我是否在最好的测试上进行了测试。
vandamon taigi 2013年


Knuth-Morris-Pratt具有O(m + 2 * n) ...您的意思是O(m + n)。
Jules

选择算法复杂度高的一个,然后使用探查器对其进行微调-一直对我有用。:-D

Answers:


38

这取决于您要执行的搜索类型。每种算法对于某些类型的搜索都表现特别出色,但是您尚未说明搜索的上下文。

以下是有关搜索类型的一些典型想法:

  • Boyer-Moore:通过预先分析图案并从右到左进行比较来工作。如果发生不匹配,则使用初始分析来确定可将模式移动到搜索文本的距离。这对于长搜索模式特别有效。特别是,它可以是次线性的,因为您不需要阅读文本的每个字符。

  • Knuth-Morris-Pratt:还预先分析了模式,但尝试重用模式初始部分中已经匹配的内容,以免重新匹配。如果您的字母较小(例如DNA基数),这可以很好地工作,因为您更有可能会发现搜索模式包含可重复使用的子模式。

  • Aho-Corasick:需要大量预处理,但是需要进行多种处理。如果您知道您将一遍又一遍地寻找相同的搜索模式,那么这比其他搜索模式要好得多,因为您只需要分析一次模式,而不是每次搜索一次。

因此,像CS中一样,没有对整体最佳解决方案的明确答案。而是为手头的工作选择合适的工具。

关于最坏情况推理的另一个说明:请考虑创建最坏情况所需的搜索类型,并仔细考虑这些搜索是否与您的情况确实相关。例如,O(mn)Boyer-Moore算法的最坏情况下的复杂性源于一个搜索模式和一个仅使用一个字符的文本(例如aaa在中查找aaaaaaaaaaaaaaaaaaaaa)-您真的需要对这种搜索速度要快吗?


我可以使用整个英文字母,并且我更新了Question,对不起,在开始时不以此开头。
vandamon taigi 2013年

是的,即使这样的搜索,我也必须要快
vandamon taigi 2013年

1

尽管我回答这个问题有点迟了,但是我认为Z-Algorithm它比其他任何同行都快。其最坏情况下的复杂度为O(m + n),并且不需要对模式/文本进行预处理。与其他算法相比,编码也非常容易。

它以以下方式工作。

例如,有一个字符串S ='abaaba'。我们将寻找的z(i)价值i=0 to len(S)-1。在进行解释之前,让我先列出一些定义。

z(i)=不。的前缀的字符与的前缀S匹配s(i)

s(i)=的ith后缀S

以下是的s(i)s = 'abaaba'

s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'

z值分别是

z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1

要详细了解该算法,请参考以下链接。

http://codeforces.com/blog/entry/3107

https://www.youtube.com/watch?v=MFK0WYeVEag

现在,需要O(N)来查找所有z值,而无需任何预处理开销。有人会怀疑现在如何使用这种逻辑来匹配给定字符串中的模式?

让我们看一个例子。模式(P): aba,文本(T): aacbabcabaad

以P $ T的形式输入。($-任何没有出现在图案或文本中的字符。我稍后会谈到它的重要性$。)

P$T = aba$aacbabcabaad

我们知道len(P)= 3。

的所有z值P$T

z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0

现在哪个z(i)= len(P)Ans = 11.因此我们的模式出现在Ans-len(P)-1= 7-1是为了$品格。

现在为什么$或任何这样的特殊字符很重要。考虑P = 'aaa'T = 'aaaaaaa'。没有特殊字符,所有z(i)值都将具有增量值。仍然可以使用以下公式找到图案在文本中的位置:

条件:z(i)> = len(P)和位置:Ans-len(P)。但是这种情况下的情况变得有些棘手和令人困惑。我个人更喜欢使用特殊字符技术。


1
您能在这里自己解释吗?具有指向外部站点的链接可以用来进行详细说明,但是答案的核心应该在于答案本身,而不是必须跟踪指向另一个站点的链接。

z算法与kmp基本相同。我怀疑它会快得多。
托马斯·阿勒

2
我同意@ThomasAhle。计算z 预处理。不过,这是一个很好的解释。O(n)由于这个答案,我提出了一种从KMP预处理转换为Z预处理的方法。在这里
leewz

-1

使用内容可寻址内存,该内存在软件中以虚拟寻址的形式实现(将字母指向字母)。

对于平均字符串匹配算法来说,这有点多余。

CAM可以同时匹配大量模式,最多可以匹配128个字母的模式(如果是ASCII,则只有Unicode 64)。这是您要匹配的字符串中每个字母长度的一次调用,并且每个最大模式长度的长度从内存中随机读取一次。因此,如果您要分析一个100,000个字母的字符串,同时具有多达9,000万个模式(要花大约128 GiB来存储大量的模式),则需要从RAM进行1,280万次随机读取,因此发生时间为1毫秒。

这是虚拟寻址的工作方式。

如果我以代表第一个字母的256个起始地址开始,则这些字母指向下一个字母的256个。如果不存在模式,则不存储它。

因此,如果我一直将字母链接到字母,就好比有128个虚拟寻址片指向虚拟寻址。

这将行之有效-但要获得900,000,000个模式同时进行匹配,则要添加最后一个技巧-并且它利用了这样一个事实,即您开始大量重复使用这些字母缓冲区,但后来又分散了。如果您列出内容,而不是分配全部256个字符,那么它的速度将非常缓慢,并且容量将增加100倍,因为最终您基本上只会在每个字母指针缓冲区中使用1个字母(我称之为“逃逸')。

如果要获得最接近的字符串匹配项,则其中的许多字符串将并行运行并收集在一个层次结构中,因此可以将错误无偏地散布出去。如果您尝试只与其中一个相邻,则您偏向树的起点。


4
@MagnusRobertCarlWoot假定您具有与roucer81相同的gavatar,这要么是哈希码冲突的天文巧合,要么是您具有相同的电子邮件地址。如果两个帐户背后的个人相同,则应使用“与我们联系”表格将其合并,以使您对通过对该答案进行投票获得的声誉获得适当的信誉。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.