KMP字符串匹配算法

KMP字符串匹配算法

蛮力匹配

基本原理

​ 两个字符串依次比对,遇到匹配失败后,短的回位至0位,长的在原基础上后移一位,继续匹配,直至完全匹配成功,输出首位位置。

UTOOLS1593590789811.png

【注】

​ 对原基础的理解:

​ 有时,会存在前面几个都匹配成功,但后面出现匹配失败时,记位的变量已经“走了”一段,故应先减去已走部分(也即回到原基础上),再后移一位。

UTOOLS1593591459612.png

代码

int Index(char* chang, char* duan){
    int c = 0,d = 0;
    while (c < strlen(chang) && d < strlen(duan)){
        if(chang[c] == duan[d]){//若匹配
            c++;
            d++;
        }else{//否则
            c = c - d + 1;//回到原基础上,再后移一位
            d = 0;
        }
    }
    return d == strlen(duan) ? c - d : -1;
}

KMP

基本原理

UTOOLS1593671677660.png 如图所示,当第一次匹配到f时匹配失败,前面成功,因此可以知道长的字符串b,c位置为b,c,此后就无需再用短字符串的a与b,c匹配,直接用a与匹配失败位置的d匹配即可,若直接失败,就后移一位即可,此后类似,直至匹配成功。

UTOOLS1593671821590.png 此时跳过了c,f,个数为2,也即匹配成功的字符串的最长前后缀的长度(棕色部分地长度,为2)。

通俗的讲,长串与短串依次分别加1,若发生失配,长串不回溯,短串回溯到与失配字符前面子串的最长公共前后缀长度相同的位置,如图,最长公共子串的长度为2,则短串回溯到2的位置,也即从c开始继续匹配。

代码

(非完整)

int IndexKmpMinus1(char* chang, char* duan){
    int* next = genNext(duan);
    int c = 0,d = 0;
    while (c < strlen(chang) && d < strlen(duan)){
        if(chang[c] == duan[d]){//若匹配
            c++;
            d++;
        }else{//否则
 			d = next[d];
        }
    }
    return d == strlen(duan) ? c - d : -1;
}

next表

手动计算

(非完整)

UTOOLS1593671787491.png

此处分析:若0号位对应的next表的值还是0,当d=0时,进入死循环,d一直被赋值为0,又因为长串位置本身就不变,这种情况下短串位置也不变,则卡死。

解决方法1(就让0号位的next表值为0),代码:

int IndexKmpMinus1(char* chang, char* duan){
    int* next = genNext(duan);
    int c = 0,d = 0;
    while (c < strlen(chang) && d < strlen(duan)){
        
        if(d == 0 && chang[c] != duan[d])//解决方法
            c++;	
        
        if(chang[c] == duan[d]){//若匹配
            c++;
            d++;
        }else{//否则
 			d = next[d];
        }
    }
    return d == strlen(duan) ? c - d : -1;
}

解决方法2(改变0号位的next表值为-1,真正的KMP),代码:

int IndexKmpMinus2(char* chang, char* duan){
    int* next = genNext(duan);
    int c = 0,d = 0;
    while (c < strlen(chang) && d < strlen(duan)){
        if(d == -1 || chang[c] == duan[d]){//若匹配
            c++;
            d++;
        }else{//否则
 			d = next[d];
        }
    }
    return d == strlen(duan) ? c - d : -1;
}

UTOOLS1593671844118.png

next表算法

基本思路为:自己与自己匹配

一个看作长串(c),一个看作短串(d)

​ 若匹配:next[c+1]=next[c]+1=d+1

UTOOLS1593671975467.png

失配:d=next[d]

UTOOLS1593671931307.png

代码:

int* genNext (char* duan ){
	int* next = (int*)calloc(strlen (duan), sizeof(int));
    
    next[0] = -1;
    int c = 0, d = -1;
    while ( c < (int)strlen (duan) - 1){
        if (d == -1 || duan[c] == duan[d] ){
            c++;
            d++;
            next[c] = d;
        }else
        	d = next[d];
    }
    return next;
}

一些问题及改进:

UTOOLS1593672008371.png

UTOOLS1593672036913.png这不好!因此如果出现这种情况就跳过去找下一个next表值。

改进代码:

int* genNext (char* duan ){
	int* next = (int*)calloc(strlen (duan), sizeof(int));
    
    next[0] = -1;
    int c = 0, d = -1;
    while ( c < (int)strlen (duan) - 1){
        if (d == -1 || duan[c] == duan[d] ){
            c++;
            d++;
            
            next[c] = (duan[c] != duan[d] ? d : next[d];
            
        }else
        	d = next[d];
    }
    return next;
}

上述过程可以用next表表示为:

UTOOLS1593672069718.png

over

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值