KMP模式匹配算法

KMP模式匹配算法

普通的字符串匹配算法非常的低效,下面我为大家讲解KMP快速匹配算法。

讲KMP算法之前,我先讲一下普通的匹配算法

简单的说,普通的算法要对字符串进行匹配,就要对S进行大循环,直到匹配成功或者全部遍历为止。

char *S = '12351234';
char *T = '1234';
复制代码

完整代码

#include <stdio.h>
#include <string.h>
int sel(char * S,char *T){
    int i=0,j=0;
    while (i<strlen(S) && j<strlen(T)) {//循环开始
        if (S[i]==T[j]) {//首字符对比
            i++;
            j++;
        }else{
            i=i-j+1;//匹配失败,回到上次匹配的下一位
            j=0;//从T的首字符和S的下一位字符再继续匹配
        }
    }

    if (j==strlen(T)) {//当j等于T的长度时说明匹配成功返回位置
        return i-strlen(T)+1;
    }
    return 0;
}
int main() {
    int add=sel("12351234", "1234");
    printf("%d",add);
    return 0;
}
//运行结果: 5
复制代码

普通算法流程

S: 1 2 3 5 1 2 3 4 //第一次 i=1, j=1

T: 1 2 3 4

S: 1 2 3 5 1 2 3 4 //第二次 i=2, j=2

T: 1 2 3 4

S: 1 2 3 5 1 2 3 4 //第三次 i=3,j=3

T: 1 2 3 4

S: 1 2 3 5 1 2 3 4 //第四次失败i=1, j=0

T: 1 2 3 4

...

普通算法会无脑的循环匹配,其实效率是很低的,大家有没有发现,只要匹配失败就会从T的第i位开始从新匹配S的第一位。

S: 1 2 3 5 1 2 3 4 //第一次 i=1, j=1

T: 1 2 3 4

当T[0]=1, T[1]=2, S[1]=2, 显然T[0]≠T[1], T[1]=S[1],这里就是KMP算法的关键了,去除多余的判断。

KMP算法对比普通匹配算法的优势是:在保证i指针不回溯的前提下,当匹配失败时,让匹配字符串向右移动最大的距离。

要保证i指针不回溯且实现功能,就只能让j指针回溯。j指针回溯的距离,就是T字符串向右移动的距离,在T中每个字符对应j指针的位置可以用算法得出,并将得出结果存在一个数组中(数组名为 next)

每个匹配的字符串(T),第一个对应的值为0,第二个对应的值为1。

例如:求Y(1,2,3,1,2,4,5)的next,前两个字符对应的0和1是固定的。

第三个字符 “3” 提取字符“12”,“1”和“2”不相等,相同的个数为0,0+1=1,所以“3”对应的next是1。

第四个字符 “1” 提取字符“123”,首先“1”和“3”不想等,相同个数为0,0+1=2,所以“1” 对应的next是1。

第五个字符 “2” 提取字符“1231”,第一个“1”和最后一个“1”相同,相同个数为1,1+1=2,所以“2” 对应的next是2。

第六个字符 “4” 提取字符“12312”,前面两个“12”和后面两个“12”相同,相同个数为2,2+1=3,所以“4” 对应的next是3。

第七个字符 “5” 提取字符“123124”,第一个字符“1”和最后一个字符“4”不相等,相同个数为0,0+1=1,所以“5” 对应的next是1。

最终得出next数组结果值为(0,1,1,1,2,3,1)

具体算法如下

Y(下标从1开始):“1231245”
next数组(下标从1开始):01

第三个字符为“3”:由于前一个字符“2”的next的值为1,取Y[1]=“1”和“2”比较,不相等,继续;
由于next[1]=0,结束。“3”对应的next值为1。(只要循环到next[1]=0,该字符的next都为1)
复制代码
Y:“1231245”
next数组:011

第四个字符为“1”:由于前一个字符“3”的next的值为1,取Y[1]=“1”和“3”比较,不相等,继续;
由于next[1]=0,结束。“1”对应的next值为1。
复制代码
Y:“1231245”
next数组:0111

第五个字符为“2”:由于前一个字符“1”的next的值为1,取Y[1]=“1”和“1”比较,相等,1(前一个
字符“1”的next值)+1=2;“2”对应的next值为2。
复制代码
Y:“1231245”
next数组:01112

第六个字符为“4”:由于前一个字符“2”的next的值为2,取Y[2]=“2”和“2”比较,相等,2
(前一个字符“2”的next)+1=3“4”对应的next值为3。
复制代码
Y:“1231245”
next数组:011123

第七个字符为“5”:由于前一个字符“4”的next的值为3,取Y[3]=“3”和“4”比较,不相等,继续,
由于next[3]=1,所以取Y[1]=“1”和4比较,不相等,结束;“2”对应的next值为2。
复制代码

最终得出next数组结果值为(0,1,1,1,2,3,1)

算法实现 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];
        }
    }
}

复制代码

kmp算法

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)) {
    
        if (j==0 || S[i-1]==T[j-1]) {//如果j==0那么表示不匹配,指针向下移动
        //如果匹配成功指针也向下移动
            i++;
            j++;
        }
        else{
            j=next[j];
        }
    }
    if (j>strlen(T)) {
        return i-(int)strlen(T);
    }
    return -1;
}
//主体
int main() {
    int i=KMP("ababcabcacbab","abcac");
    printf("%d",i);
    return 0;
}

复制代码

运行结果:6

优化next数组

例如:

T: a b c a c

next 0 1 1 1 2

在T中,有两个字符“a”,我们假设第一个为 a1,第二个为 a2。在程序匹配过程中,如果 j 指针指向 a2 时匹配失败,那么此时, i 指针不动,j 指针指向 a1 ,很明显,由于 a1==a2,而 a2!=S[i],所以 a1 也肯定不等于 S[i]。

为了避免不必要的判断,需要对 next 数组进行精简,对于T来说,由于 T[4] == T[next[4]] ,所以,可以将next数组改为:

T: a b c a c

next :0 1 1 0 2 //前面说过,如果j==0那么表示不匹配,j和i指针向下移动

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++;
            if (T[i-1]!=T[j-1]) {
               next[i]=j;
            }
            else{
                next[i]=next[j];
            }
        }else{
            j=next[j];
        }
    }
}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值