BF算法和KMP算法对比

BF算法和KMP算法

    子串的定位操作通常被称作串的模式匹配,所谓的模式匹配即从较长的字符串S(主串)的pos位置处开始寻找字符串P(模式串)首次出现的位置,模式匹配算法经典的有BF算法和KMP算法。

1、BF算法

    BF算法的思想是从主串的第pos个字符起和模式的第一字符比较,若相等,则继续比较后续字符;若主串与模式串不是完全匹配,则回溯至主串的第pos+1个字符起和模式的第一个字符比较,若相等,则继续比较后续字符,否则继续重复上述回溯机制,直至找到主串中第一次包含子串的的位置,其图例如下:

《BF算法和KMP算法对比》


    因此,BF算法如下所示:

《BF算法和KMP算法对比》

2、KMP算法引文——BF算法分析

    由上面的分析可知,BF算法是基于主串指针回溯,重新与子串进行逐字符进行比较,主串为S什么要进行回溯呢,原因在于模式P中存在相同的字符或者说由字符(串)存在重复(模式的部分匹配性质),设想如果模式P中字符各不相同,主串就S的指针就根本不需要回溯;然而,我们可以发现在主串S与模式发生失配时,主串指针进行回溯会影响效率,因为由于模式S本身字符的部分部分匹配性质,回溯之后,主串S与模式P有些部分比较是没有必要的,这就是对BF算法所要改进的地方。

      

3、KMP算法原理

    KMP算法就是针对BF算法的缺点所提出的,其关键就是,主串S与模式P发生失配时,如果主串S指针不进行回溯,那么下面应该与模式中那个位置的字符进行比较呢?一旦在模式中找到这样的位置,说明我们不需要每次与模式P重新开始匹配,这就意味着KMP算法会直接跨过较多的位数,起到加速的作用。

    从上面的分析我们可以隐约感觉到,当S[i]与P[j]发生失配时,与S[i]进行下一次比较的P[next(j)](next(j)表示发生失配时,与S[i]进行的比较的模式P中的字符位置),是由模式P本身固有的性质决定的,即上文2所提的BF回溯的原因,模式P本身的部分匹配性质。这就意外着,只要模式P给定,模式P与任意给定的主串发生失配时,模式P每一位对应的跳转位置是固定的,即可以事先确定。用数学语言来说就是,模式P通过自身的部分匹配性质映射了一个相同长度的跳转位置数组(专业叫后缀数组,文中用next数组表示),下面我们首先分析确定next数组的数学原理。


S:S0, S1, S2,…, Si-j, Si-j+1, …………….., Si-1, Si, Si+1,…….., Sn

P:           P0, P1,P2, ………………, Pj-1, Pj,………,Pm

P:                   P0, P1, P2, ……, Pk-1,Pk,……,Pj-k, Pj-k+1…, Pj-1,Pj, ………Pm

 

    假设S[i]与P[j]发生失配(以红色表示),我们找到了跳转的位置k,即next[j]=k(蓝色表示),则可以得到下面几个数学关系:

    失配时:P[0,1,2……,j-2,j-1] = S[i-j,i-j+1,…….,i-2,i-1];(1)

    跳转后:P[0,1,2……,k-2,k-1] = S[i-k,i-k+1,…….,i-2,i-1];(2)

    结合(1)、(2)得:

S[i-k,i-k+1,…….,i-2,i-1]= P[j-k,j-k+1,……,j-2,j-1] = P[0,1,2……,k-2,k-1];(3)

    由(3)式可知,P[j-k,j-k+1,……,j-2,j-1] = P[0,1,2……,k-2,k-1],等号左右两边恰好是P[0,1,2……,j-2,j-1]的前缀和后缀的相等部分(好好体会前缀和后缀概念),且这个相等部分的长度等于k即next[j]。

    由上数学分析可知,KMP算法每一次匹配都是基于前一次匹配的结果(前一次的结果丰富记录了模式P的性质),那么我们就要在前一次基础上,分析模式P的重复特性,找出前缀和后缀相同部分的长度求出next数组,下面给出next数组的递推算法。

      

4、next数组与KMP算法

    我们看到,求取next数组是KMP算法的核心,我们假设已知k=next[j],即有:

P[j-k,j-k+1,……,j-2,j-1]= P[0,1,2……,k-2,k-1]

则S[i+1]与P[j+1]发生失配时:

    (1)P[j]=P[k],则表明模式P中:

P[j-k,j-k+1,……,j-2,j-1,j]= P[0,1,2……,k-2,k-1,k]

显然,我们容易得到:next[j+1] = k + 1=next[j]+ 1;

    (2)P[j]!=P[k],则表明模式串P中:

P[j-k,j-k+1,……,j-2,j-1,j]!= P[0,1,2……,k-2,k-1,k]

此时将求next数组的问题转化一个模式匹配问题,由上式的形式可以看出,将模式串P既看做主串有看做模式串,则在当前的模式匹配中,已经有:

P[j-k,j-k+1,……,j-2,j-1]= P[0,1,2……,k-2,k-1]

则由(1)的思想,若next[k]=k′且P[j]=P[k′],则:

P[j- k′,j- k′+1,……,j-2,j-1,j]= P[0,1,2……, k′-2, k′-1, k′]

    这就是说,next[j+1]= k′ + 1 = next[k] + 1。同理,若P[j]!=P[k′],继续按执行(2)的思想执行。

    按照上述递推思想得到获取next数组的算法如下:

《BF算法和KMP算法对比》

    上面的算法还任然有缺陷,若S[i]与P[j]发生失配时,若出现,P[j]=P[next(j)]时,还按照上述算法进行,仍然不是最有效率的,此时可以跳过更多的步数,具体修改如下:

《BF算法和KMP算法对比》

    则完整的KMP算法如下:

《BF算法和KMP算法对比》

 

5、时间和空间复杂度分析

算法

时间复杂度

空间复杂度

BF

slen*plen

1

KMP

slen+plen

plen

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/Tsou_Joyce/article/details/20730305
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞