1.BM之性能
接下来,针对已经综合了 bc 和 gs 两种策略的 BM 算法,标定它对应的复杂度,并将这种算法与此前的 KMP 以及蛮力算法在性能上做一个综合的对比分析。
首先是 BM 算法本身的性能。
- 在空间方面,除了模式串和文本串本身,我们还需要附加一张 bc 表以及另一张 gs 表,前者的规模线性正比于字母表的规模,而后者的规模则线性正比于模式串本身。
- 预处理的成本主要消耗于这两张表的构造过程。我们知道它们都各自可以在线性的时间内完成。
就我们最为关心的查找操作而言,我们已经看到,哪怕仅仅是凭借 bc 策略,我们也可以在最好情况下实现 O(n/m) 的性能。尽管在只采用 bc 策略时,我们在最坏情况下有可能会退化到 O(n * m)的时间效率,但在平行的引入了 gs 策略之后,这种最坏情况将会得到杜绝。
实际上,更为精细的分析表明,在同时兼顾了 bc 和 gs 策略之后,BM算法即便在最坏情况下的运行时间,也不会超过线性。
2.各算法纵览
在结束本节之前,让我们通过这样一组图,来对串匹配的各种典型算法,在性能上,作一对比。
这里的纵轴表示运行时间,而这3个标尺由低到高分别表示 n/m,n +m 以及 n *m。
正如我们已经知道的,对于蛮力算法而言:在最坏情况下的运行时间将达到最多的n*m。然而,我们也曾指出,蛮力算法在最好情况下的运行时间也大致在线性的幅度。在在这两种极端情况之间,最大的一个影响因素,实际上是某个概率。也就所谓单次比对成功概率,就蛮力算法而言,这个概率越高,它也就越容易误入歧途,从而导致非常高的时间复杂度。反过来,在这个概率并不是很高的时候,蛮力算法的性能将很自然地接近于线性。
实际上,在通常的意义上。决定这一概率值的最大因素莫过于字母表本省的规模。实际上,单次匹配成功的概率大致与字母表的规模成反比。
我们再来看 KMP算法:我们已经证明,无论在什么情况下,它的性能都始终稳定在线性的水平。
由此可见,只有在字符表规模非常小的情况下,KMP算法相对于蛮力算法在性能上的优势才会充分地体现出。
这幅图(由左至右第三个)是仅采用 bc 策略的 BM 算法,可以看到,它非常适用于大字符集。当单词匹配成功的概率极低时,它的性能将接近于O(n/m)。当然在字符表规模很小时,bc 策略依然很容易误入歧途,从而导致极高的 n*m 的复杂度。
而只有在将 bc 策略与 gs 策略联合使用时(由左至右第四个),二者才可以相得益彰。可以看到,联合使用这两种策略,在最好情况下,我们依然可以实现O(n/m)的运行时间。同时, BC 策略的缺点也会得到有效地抑制,并保证在最坏情况下,运行时间也不超过线性。
也可以从性能的维度,从最低到最高,划分为3个阶次。
- 这是蛮力算法(BF),它在最好情况下也不过线性。而在一般,甚至最坏情况下,它都需要O(n * m) 的时间。
- 这里是 bc 策略,可以看到,它的性能变化幅度极大,从最好的O(n /m) 一直到最坏的O(n * m)。
- 而 KMP在这,可以看到,它是中规中矩的,始终保持线性的时间复杂度。
- 最后是融合了 bc 和 gs 两种策略的 BM 算法,我们可以看到,在最坏的情况下,它也只需线性的运行时间。而在最好的情况下,它甚至可以达到 O(n /m) 。