KMP算法详解

详情见代码

友情附上传送门,帮助理解
三哥视频讲解
一位Up主视频

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>

void prefix_table(char [],int  ,char []);
void Next(char [],int ,char [] );
bool KMP_search(char [],int ,char [],char [],int ,int *);
//该函数求的是pattern字符串的前缀表pre[]
void prefix_table(char  pattern[],int len,char prefix[]){
/*  
    前缀表prefix[]的第一个值一定为0,即pre[0]=0;
    取两个下标,i,j,i从1开始,j从0开始
    1.当i,j所对应的字符相等时,i++,j++
    2.当i与j所对应的字符不相等时,又分为两种情况:
        a.当j不是指向第一个位置即j=0时,则j=prefix[j-1],此时会继续比较,如果i,j所指向的字符还是不相等,则继续将j=prefix[j-1],直到j=0进入b情况
        b.当j=0时,此时j=prefix[j-1]中的j-1越界,所以不可以做a情况中的操作,即直接将prefix[i]=0,,并且将i+1
*/
    int i=1,j=0;
    prefix[0]=0;
    while(i<len){
        if(pattern[i]==pattern[j]){
            prefix[i]=j+1;
            i++;
            j++;
        }
        else{
            if(j!=0){
                j=prefix[j-1];
            }
            else{
                prefix[i]=0;
                i++;
            }
        }     
    }           
}

//该函数得到最终的next数组(解释一下next数组时什么,即当主串中第i位与待匹配串第j位发生失配时,主串i不动,j下次取值的值的集合即为next[])
//这里的len是prefix[]的长度,即pattern字符串的长度
void Next(char prefix[],int len,char next[]){               
/*
    如何从prefix[]变成next[]?
    即将prefix[]整体向后移一步,并在第一位赋值-1,即next[0]=-1;
*/    
    int i;
    for(i=0;i<len;++i){                       
        if(i==0) {
            next[i]=-1;
            continue;
        }
        next[i]=prefix[i-1];
    } 
}

//KMP算法,其中len1代表主串长度,len2代表next[]长度(注:pattern长度等于prefix[]长度等于next[]长度)
bool KMP_search(char text[],int len1,char pattern[],char next[],int len2,int *pVal){
/*
    首先由前面步骤可得待匹配串的next[];
    i指向主串第一个字符,j指向待匹配串第一个字符
    1.当i,j所指向的字符相等时,i++,j++
    2.当i,j所指向的字符不相等时,分为两种情况:
        a.当j不是指向pattern的第一个位置时,j=next[i],然后再进行比较,如果此时i,j所指向的字符还是不相等,那么j继续等于next[j],直到j=0,进入b情况
        b.当j=0时,此时也代表了待匹配串的第一个字符与当前字符不匹配,所以i,j同时加1
*/
    int i=0,j=0;
    while(i<len1&&j<len2){
        if(text[i]==pattern[j]){
            i++;
            j++;
        }
        else{
            if(j!=0) {
                j=next[j];
            }
            else{
                i++;
            }
        }
    }
    if(j==len2){
        *pVal=i-len2;                //最终返回的i-len2+1即为一开始匹配的字符位置
        return true;
    }
    return false;
}


int main(){
    char text[]="adbcefda";                 
    char pattern[]="bcef";
    int val;                 
    int m=sizeof(text)-1;
    int n=sizeof(pattern)-1;
    char pre[n],next[n];                   //pre[]存放前缀表的值,next[]存放的是最终需要的值
    prefix_table(pattern,n,pre);                       
    Next(pre,n,next);
    if(KMP_search(text,m,pattern,next,n,&val)){
        printf("匹配出现在第%d个位置",val+1);
    }
    else{
        printf("主串中不存在与待匹配串相匹配的子串");
    }
    return 0;
}

/*前缀表如何求?
例如,字符串string[]="abcabcd";
其所有前缀集合为:
"每一行序列中最大公共前后缀放入前缀表中",即prefix[]={0,0,0,1,2,3,0}
        0        a
        0        ab
        0        abc
        1        abca
        2        abcab
        3        abcabc
        0        abcabcd
*/

kmp算法的核心是求得最大公共前后缀的值,那么如何求的最大公共前后缀呢,这里附上我笔算的一段内容
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值