BF算法和KMP算法

前言

设有两个字符串S,T。S为主串,T为子串也可称为模式。在S中查找与模式T相符的子串如果匹配成功,则确定T在主串S中的位置。

著名的模式匹配算法就是BF和KMP。

这两种算法都是用于子串的定位运算,通常称串的模式匹配或串匹配,其用处也非常广泛。如:拼写检测,语言翻译,搜索引擎等等都有用到。

 

1.BF算法

BF算法最简单直观。

算法的步骤:

1.就是利用两个指针(这里记为指针i和指针j),分别指向主串S,和子串T的当前位置,i的初值为主串S中查找的起始位置,而j初值为T的起始位置(这里设S,T起始位置均为1)。

2.然后比较S[i]和T[j]是否相等。

若相等则,i和j分别指向下一个位置,继续比较下一个字符。

若不相等,指针i退回到(i=i-j+2)处,指针j退回到起始位置1

3.最后如果j>=T.length,就说明T在S中出现,匹配成功。否则就匹配失败。

 

再看看时间复杂度

当在最好的情况下,即当每次匹配失败都发生在模式串的第一个字符,此时复杂度为:e4d0693e9da941ab901f5f9ea3679372.png

也就是O(n+m)。

而当最坏情况下,即当每次匹配失败都发生在模式串的最后一个字符,此时复杂度为:b521ac73740e4bb7b85d7c45708ed8dd.png

时间复杂度为O(n*m)

虽然BF的时间复杂度为(n*m),但在一般情况下,其实际执行时间近似O(n+m),所以才能至今仍被使用。

2.KMP算法

KMP就有些复杂了,但它可以在时间复杂度为O(n+m)的情况下完成模式匹配。

因为它无需回溯主串的指针。

简单理解其原理:

就是开始时正常匹配,而当主串S指针i和子串T指针j 字符匹配失败时,

在这里还有两种情况:

第一种:匹配的第一个字符就不相同,则此时就让i+1,j不变,继续匹配。

第二种:匹配失败时 j>1,看看T中指针j-1(我们可以将j-1记为k,看作一个新指针) 前面(k-1)个字符是否与i指针前面的(k-1)个字符是否相等,

若不相等,再看指针k=k-1前面(k-2)个字符是否与i指针前面的(k-2)个字符是否相等,再判

断,直至k=1(起始位置),也就是直到k前面没有字符;

当k=1时,就将k赋给j,继续与i位置下的字符进行比较,匹配失败,就让i+1,j不变,继

续匹配

若相等,只需将指针k赋给j再与指针i位置下的字符进行比较;

#为什么是指针j-1(指针k)前面的字符呢?

稍微想想就能理解,如果是j前面的字符,不就和最初匹配时没什么区别了嘛

 

next

但是只是新加一个指针,时间复杂度可不会只为O(n+m)。有什么更好的方法吗?

我们可以发现让指针k前面的字符串与指针i前面(k-1)个字符进行比较,其实就是与指针j前面(k-1)个字符进行比较。而子串T我们是已知的。所以我们在子串与主串S匹配之前,就能知道k前面字符串是否与i前面的字符串相等。

由上面的原理可以引出next函数 { 0 j=1 next[j] { { k | 1<k<j } { 1 k=1

所以我们在匹配前就可以算出j在每个位置下,k为多少时 前面的字符都与i前面的字符相等。我们可以用next[j]=k,来储存k。然后在匹配失败时将next[j]赋给j,也就是将k赋给j

例:3e20350610f04ae2a3ff0588aa5f6382.png

/*next函数应该怎么求,比如说abaabcac,next[1]=0,next[2]=1,next[3]=1
    next[4]=2,next[5]=2,next[6]=3,因为在算到第2个字符,如果第二个字符
    就不匹配的,那就让i和第一个字符重新匹配,而第四个字符因为前面的第三个字符是和
    第一个字符一样的所以可以让第i个字符和next[4]个字符匹配就是看这个字符之前有几个和
    最前面的字符是相同的。*/
    int i,j;
    i=1;
    next[1]=0;
    j=0;
    while(i<T.length)
    {
        if(j==0||T.ch[i]==T.ch[j])
        {
            ++i;
            ++j;
            if(T.ch[i]!=T.ch[j])
                next[i]=j;
            else
                next[i]=next[j];
        }
        else
            j=next[j];
    }

nextval

但next函数还是有缺陷的,例如主串S="aaaabcde",子串T="aaaaax",其next数组值分别为012345,在开始时,当i=5、j=5时,我们发现“b”与“a”不相等,因此j=next[5]=4,此时“b”与第4位置的“a”依然不等,j=next[4]=3,直到j=next[1]=0时,根据算法,此时i++、j++,得到i=6、j=1。

next还是有多余的步骤,所以用nextval[j],进行修正。

 

nextval计算方法

当j=1时,nextval[1]=0

当j>1时,用当前指针j1字符,与j2=next[j1]指针的字符进行比较,若相等则nextval[j1] = nextval[j2],若不相等则nextval[j1] = next[j1]

例如:

b5d154c9f4d94aa9bc2bc92ac64dca54.png

 

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值