字符串---KMP算法(快速模式匹配算法)

@Adrian

"KMP"算法称为“快速模式匹配算法”

KMP"算法相比于"BF"算法,优势在于:

  • 在保证指针 i 不回溯的前提下,当匹配失败时,让模式串向右移动最大的距离;
  • 并且可以在O(n+m)的时间数量级上完成对串的模式匹配操作;
模式串向右移动的距离计算

在模式串和主串匹配时,各有一个指针指向当前进行匹配的字符(主串中是指针 i ,模式串中是指针 j ),在保证 i 指针不回溯的前提下,如果想实现功能,就只能让 j 指针回溯。
j 指针回溯的距离,就相当于模式串向右移动的距离。 j 指针回溯的越多,说明模式串向右移动的距离越长。
计算模式串向右移动的距离,就可以转化成:当某字符匹配失败后, j 指针回溯的位置。

对于一个给定的模式串,其中每个字符都有可能会遇到匹配失败,这时对应的 j 指针都需要回溯,具体回溯的位置其实还是由模式串本身来决定的,和主串没有关系。

模式串中的每个字符所对应 j 指针回溯的位置,可以通过算法得出,得到的结果相应地存储在一个数组中(默认数组名为 next )。

计算方法是:对于模式串中的某一字符来说,提取它前面的字符串,分别从字符串的两端查看连续相同的字符串的个数,在其基础上 +1 ,结果就是该字符对应的值。

每个模式串的第一个字符对应的值为 0 ,第二个字符对应的值为 1 。

下面举个例子:
求模式串 “abcabac” 的 next 。前两个字符对应的 0 和 1 是固定的。

对于字符 ‘c’ 来说,提取字符串 “ab” ,‘a’ 和 ‘b’ 不相等,相同的字符串的个数为 0 ,0 + 1 = 1 ,所以 ‘c’ 对应的 next 值为 1 ;

第四个字符 ‘a’ ,提取 “abc” ,从首先 ‘a’ 和 ‘c’ 就不相等,相同的个数为 0 ,0 + 1 = 1 ,所以,‘a’ 对应的 next 值为 1 ;

第五个字符 ‘b’ ,提取 “abca” ,第一个 ‘a’ 和最后一个 ‘a’ 相同,相同个数为 1 ,1 + 1 = 2 ,所以,‘b’ 对应的 next 值为 2 ;

第六个字符 ‘a’ ,提取 “abcab” ,前两个字符 “ab” 和最后两个 “ab” 相同,相同个数为 2 ,2 + 1 = 3 ,所以,‘a’ 对应的 next 值为 3 ;

最后一个字符 ‘c’ ,提取 “abcaba” ,第一个字符 ‘a’ 和最后一个 ‘a’ 相同,相同个数为 1 ,1 + 1 = 2 ,所以 ‘c’ 对应的 next 值为 2 ;

所以,字符串 “abcabac” 对应的 next 数组中的值为(0,1,1,1,2,3,2)。

上边求值过程中,每次都需要判断字符串头部和尾部相同字符的个数,而在编写算法实现时,对于某个字符来说,可以借用前一个字符的判断结果,计算当前字符对应的 next 值。

具体算法:

模式串T为(下标从1开始):“abcabac”
next数组(下标从1开始): 01

第三个字符 ‘c’ :由于前一个字符 ‘b’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘b’ 相比较,不相等,继续;由于 next[1] = 0,结束。 ‘c’ 对应的 next 值为1;(只要循环到 next[1] = 0 ,该字符的 next 值都为 1 )

模式串T为: “abcabac”
next数组(下标从1开始):011

第四个字符 ’a‘ :由于前一个字符 ‘c’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘c’ 相比较,不相等,继续;由于 next[1] = 0 ,结束。‘a’ 对应的 next 值为 1 ;

模式串T为: “abcabac”
next数组(下标从1开始):0111

第五个字符 ’b’ :由于前一个字符 ‘a’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘a’ 相比较,相等,结束。 ‘b’ 对应的 next 值为:1(前一个字符 ‘a’ 的 next 值) + 1 = 2 ;

模式串T为: “abcabac”
next数组(下标从1开始):01112

第六个字符 ‘a’ :由于前一个字符 ‘b’ 的 next 值为 2,取 T[2] = ‘b’ 和 ‘b’ 相比较,相等,所以结束。‘a’ 对应的 next 值为:2 (前一个字符 ‘b’ 的 next 值) + 1 = 3 ;

模式串T为: “abcabac”
next数组(下标从1开始):011123

第七个字符 ‘c’ :由于前一个字符 ‘a’ 的 next 值为 3 ,取 T[3] = ‘c’ 和 ‘a’ 相比较,不相等,继续;由于 next[3] = 1 ,所以取 T[1] = ‘a’ 和 ‘a’ 比较,相等,结束。‘a’ 对应的 next 值为:1 ( next[3] 的值) + 1 = 2 ;

模式串T为: “abcabac”
next数组(下标从1开始):0111232

Next算法实现:
#include <stdio.h>
#include <string.h>
void Next(char*T,int *next){
    int i=1;
    next[1]=0;
    int j=0;
    while (i<strlen(T)) {
        if (j==0||T[i-1]==T[j-1]) {
            i++;
            j++;
            next[i]=j;
        }else{
            j=next[j];
        }
    }
}
基于next的KMP算法的实现

KMP算法的运行流程(主串:ababcabcacbab,模式串:abcac)
第一次匹配:
在这里插入图片描述
第二次匹配:
在这里插入图片描述
第三次匹配:
在这里插入图片描述
第四次匹配:
在这里插入图片描述

完整代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Next(char * T,int *next){
    int i=1;
    next[1]=0;
    int j=0;
    while(i<strlen(T)){
        if(j==0||T[i-1]==T[j-1]){
            i++;
            j++;
            next[i]=j;
        }else{
            j=next[j];
        }
    }
}
int KMP(char*S,char*T){
    int next[10];
    Next(T,next);//根据模式串T,初始化next数组
    int i=1;
    int j=1;
    while(i<=strlen(S)&&j<=strlen(T)){
        //j==0:代表模式串的第一个字符就和当前测试的字符不想等:S[i-1]==T[j-1],如果对应位置字符相等,两种情况下,指向当前测试的两个指针的下标i和j都向后移
        if(j==0||S[i-1]==T[j-1]){
            i++;
            j++;
        }
        else{
            j=next[j];//如果测试的两个字符不相等,i不动,j边为当前测试字符串的next值
        }
    }
    if(j>strlen(T)){//如果条件为真,说明匹配成功
        return i-(int)strlen(T);
    }
    return -1;
}
int main()
{
    int i=KMP("ababcabcacbab","abcac");
    printf("%d",i);
    return 0;
}

输出结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值