KMP算法

        最近通过网上的资料学习了KMP算法~KMP算法是用于匹配两个字符串的一种算法,相较于最初始的逐个匹配的办法其优势就在于,KMP算法匹配字符串出现字符比较不相等时,尽可能的利用已经得到的“部分匹配”的结果,对子串“滑动”尽肯能多的距离。

        KMP算法是在已求得子串(模式串)的next数组的基础上进行匹配的。因此next数组的求解对于KMP算法来说是至关重要的。

例如:有一个子串str1[n]={a,b,a,b,b,c}需要求解next数组,那么我们先要了解字符串的前后缀是什么。

        所谓前缀,就是对应的字符串剔除最后一个字符所得的字符串。“abcca”的前缀就是“abcc”。后缀则相反,是对应的字符串剔除第一个字符所得的字符串。“abcca”的后缀为“bcca”。

        了解完前后缀的概念,我们就可以正式开始求解next数组了。当数组下标为0时,对应的字符串字符个数为1,所以一般都是将next[0]进行初始化,这里我习惯将next[0]初始化为“-1”(“-1”代表着这个字符串没有相同的最大前缀和最大后缀,也可以初始化为其他数值)。

        当数组str1[n]={a,b,a,b,b,c}下标为1时,对应的字符串为“ab”,其前缀为“a”,后缀为“b”,不相等,因此为“-1”;

        当数组str1[n]={a,b,a,b,b,c}下标为2时,对应的字符串为“aba”,其前缀为“ab”,后缀为“ba”,不相等。将前缀向左移一位,后缀向右移一位。分别为“a”,“a”,相等,则在-1的基础上+1,next[1]为“0”;

        所以next数组求解为

                               0        1        2        3        4       5

                               a        b        a        b        b       c

        下标为0,字符串为“a”,无前后缀,next[0]=-1;

        下标为1,字符串为“ab”,前缀“a”,后缀“b”,next[1]=-1;

        下标为2,字符串为“aba”,前缀“ab”,“a”,后缀“ba”,“a”,(一个相等)next[2]=0;

        下标为3,字符串为“abab”,前缀“aba”,“ab”,后缀“bab”,“ab”,(两个相等)next[3]=1;

        下标为4,字符串为“ababb”,前缀“abab”,“aba”,“ab”,“a”,后缀“babb”,“abb”,“bb”,“b”,next[4]=-1;

        下标为5,字符串为“ababbc”,前缀“ababb”,“abab”,“aba”,“ab”,“a”,后缀“babbc”,“abbc”,“bbc”,“bc”,“c”,next[5]=-1;

        所以求得next数组为{-1,-1,0,-1,-1}。

  代码实现:

 /**计算next数组长度**/
void get_next(char str1[],int next[])///str1为子串(模式串)
{
    int i=1,j=-1;
    next[0]=-1;///next[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀

    for(i=1;i<strlen(str1)-1;i++)
    {
        while (j>-1&&str1[j+1]!= str1[i])///如果下一个不同,那么j就变成next[j]
            j=next[j];///往前回溯
        if (str1[j+1]==str1[i])///如果相同,j++
        {
            j=j+1;
        }
        next[i]=j;///没有相同,next[i]=-1
    }
}

求解完next数组接下来就可以利用next数组对主串进行匹配了。

匹配的思想与next数组求解类似,i表示当主字符串的下标从0开始,j表示模式串的下标,从-1开始表示前后缀无重复。i与j+1所指字符不相同时,对j进行回溯,即当出现失配时,将j进行回溯到next[j],继续进行比较。移动模式串,直到j指到模式串的末尾。

代码为:

/*KMP算法*/

void KMP_A(char str1[],char str[])///str1为子串,str为主串
{
    int next[MAXN];
    int L1,L2;
    int i=0,j=-1;///将j初始化为-1
    L2=strlen(str1);///求取str1的数组长度
    L1=strlen(str);///求取str的数组长度
    get_next(str1,next);///求取next数组
    for(i=0;i<L1;i++)
    {
        while(j>-1&&str[i]!=str1[j+1])///str和str1不匹配,且j>-1(表示str和str1有部分匹配)
             j=next[j];///往前回溯
        if(str[i] ==str1[j+1])
             j=j+1;
        if (j==L2-1)///说明 j 移动到str1的最末端
        {
             j=-1;///重新初始化,寻找下一个
             i=i-L2+1;///i定位到该位置,外层for循环i++可以继续找下一个
        }
     }
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值