关于KMP算法的学习总结以及next数组的求法

      KMP算法是一种模式匹配算法,用于字符串寻找子串,在BF算法(暴力)中由于在匹配中有多次匹配是多余的情况,针对这种情况做的优化改进。

    首先我们需要了解几个基本的概念:字符串的前缀,后缀和部分匹配值

1、前缀是指除最后一个字符以外,字符串所有的头部子串

 2、后缀是指除第一个字符以外,字符串所有的尾部子串

 3、部分匹配值则为字符串最长相等前后缀的长度。

     以‘ababa’为例:

'a'前后缀都为空集,最长相等前后缀的长度为0;

'ab'前缀为{a},后缀为{b},最长相等前后缀的长度为0;

'aba'前缀为{a,ab},后缀为{ba,a},最长相等前后缀的长度为1;

'abab'前缀为{a,ab,aba},后缀为{bab,ab,b},最长相等前后缀的长度为2;

'ababa'前缀为{a,ab,aba,abab},后缀为{baba,aba,ba,a},最长相等前后缀的长度为3;

所以'ababa'这个字符串的部分匹配值为00123,写为PM表的形式:

编号12345
Sababa
匹配值00123

用PM表来做匹配过程:

第一次匹配:

在j=5失配,i不动,把j回退到2位,此时j=3,从图像上来看就相当于整个子串后移了两位,实际上移动的只有j;

第二次匹配:

由于不同的还是i=5的部分,i不动,j后移2位变为1,开始第三次匹配:

第四次匹配:

结束。

由于PM表在使用的时候并不方便,因此对于上述的PM表,把匹配值那一行全部右移一位,空缺补-1,再全体+1,就得到了next数组(有的题目里不+1):

编号12345
Sababa
next01123

此时,next[j]的含义是:在子串的第j个字符与主串发生失配时,j则跳到子串next[j]位置,重写与主串当前位置进行比较。

在做题的过程中一般直接求next数组,next[1]=0:

                   

在这里解释next[]数组里关于0,1,以及其他数字的具体含义:

0代表初始化,也可以理解为将主串和模式串的第1个字符前面的空位置对齐,即模式串右移一位

1代表j将回跳到模式串开头,意味着此时子串主串匹配率最低,右移位数最大

其他数字自然是代表不用从头开始匹配,可以从next[j]位置开始匹配拉

 

以上过程是简单手算的过程,代码实现的话难度有点大,而且,代码求next数组的过程和手算并不一样,要用到递归的思想,我也没有理解得很好QAQ,所以参考了王道的代码和解释:

void getNext(String T,int next[]){

    int i=1,j=0;

    next[1]=0;

    while(i<T.length){

       if(j==0||T.data[i]==T.data[j]){

       ++i;++j;

        next[i]=j;//若T[i]==T[j],则next[j+1]=next[j]+1

       }

       else  j=next[j];//否则令j=next[j],循环继续

                               }

}

KMP算法的优化:(比如像'aaaab'匹配'aaabaaaaab'的情况还可以进一步优化)

打字太多了懒得搞了,直接上个公式做题把Q_Q

next数组更名为nextval数组,分两种情况:

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

j>1时:

    若T[ j ]!=T[ next[j] ]        nextval[ j ]=next[ j ]

    若T[ j ]==T[ next[j] ]        nextval[ j ]=nextval[ next[j] ]

 

总结:KMP算法复杂度是O(m+n),虽然暴力匹配是O(mn),但在一般情况下近似为O(M+N),而且KMP算法仅在主串和子串有很多部分匹配下才现得比暴力匹配快,主要优点是主串不回溯。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值