KMP过程原理说明与实现——跳转表是核心!

一:KMP过程原理图示

图1,上方主串与下方模式串出现不匹配字符D,则如下图所示,因模式串D前方的AB与模式串的前缀AB重复,则模式串跳转到当前位置的前方最长重复前缀即AB的后一个位置C,继续比较。

图2

图3,以此类推,直到模式串全部匹配或者主串结束

(以上三个图来自阮一峰博客

 

二 :KMP实现——见码如见人

#include <stdlib.h>
#include <stdio.h>
//双指针(查重指针tail前缀指针pre)比较公共前缀,最终生成模式串每个位置与主串某位置不匹配时的模式串跳转表,
//跳转的位置是当前位置的前方所匹配的最长公共前缀的后一个位置,跳转后继续比较
void getNext(char P[],int len, int next[])
{
    int tail = 1, pre = 0,lastChar = 1,frontChar = 0;
    next[1] = 0;
    while(tail < len){
        //若两处相等,意味着模式串中[以查重指针所在位置为尾部的子串tailstr]与[模式串某前缀prefix]相同(tailstr与prefix长度相等),
        //那么只要给查重指针保存这个prefix尾部的位置(即设置跳转位),在主串与模式串比较时就可以用来移动模式串了。
        //移动过程是:若主串当前位置s与模式串当前位置p不匹配,则下一次比较时模式串可以直接跳到模式串p位置的跳转位pn处,
        //让跳转位再跟主串当前位置继续比较。
        //移动的结果是此时模式串原不匹配位置p变成跳转位pn,(此时模式串的跳转位前方跟主串对应位置是相同的)。
        //如果移动后主串与模式串依然不匹配,则模式串继续移动到跳转位。其含义是——若当前prefix这个子串本身依然含有重复的前缀prefix2,
        //则模式串指针移动到prefix2之后的一个位置,再次比较。以此第推,直到主串结束或者模式串移动到头部(然后继续比较直到主串结束)
        //注意:上面提到的前缀prefix或长或短,因为比较此位置前可能此位置的值已经重复了多次
        if(pre == 0 || P[lastChar] == P[frontChar]){
            ++tail;
            //相同时前缀指针指向当前所匹配(重复)前缀的后一个位置(这个位置叫跳转位),意味着跳转位前方是某前缀的重复值,
            //其意义是使主串不再比较模式串中重复的前缀(而直接对齐),这也是跳转表的作用
            ++pre;
            next[tail] = pre;//为每一位设置跳转位
            
        }else{
            pre = next[pre];//模式串中公共前缀内部可能自身依然含有公共前缀
        }
        lastChar = tail-1;//需要比较查重指针与前缀指针前一位置是否相同【跳转位是其前方所重复前缀的后一位置,如上图所示,当前位不匹配时跳转到其前方所重复前缀的后一位置】
        frontChar = pre-1;

    }
}
int match(char S[], int sLen, char P[], int pLen, int next[])
{
    int i = 0, j=0;
    while(i < sLen && j < pLen){
        if(j == 0 || S[i] == P[j]){
            i++;j++;
        }else{
            j = next[j];
        }
    }
    
    if(j==pLen){
        return i-pLen;
    }
    return 0;
}
//简单匹配——可用来对KMP结果验证
int match0(char S[], int sLen, char P[], int pLen)
{
    int i = 0, j = 0;
    while ( i < sLen && j < pLen){
        if(S[i] == P[j]){
            i++;j++;
        }else{
            i = i-j+1;
            j=0;
        }
    }
    int pos = -1;//负数表示不匹配
    if (j == pLen){
        pos = i - pLen;
    }
    return pos;

}
int main(int argc, char const *argv[])
{
#define S_LENGTH 22
#define P_LENGTH 4
    char P[S_LENGTH] = "abbc";//尾部
    // char P[S_LENGTH] = "aaac";//中间
    // char P[P_LENGTH] = "abca";//头部
    char S[S_LENGTH] = "abcabaaacbcacbabaaabbc";
    int next[P_LENGTH] = {0};

    getNext(P, P_LENGTH, next);
    
    int pos = match(S, S_LENGTH, P, P_LENGTH, next);
    printf("KMP:the first matched position is %d\n", pos);

        int pos0 = match0(S, S_LENGTH, P, P_LENGTH);
    printf("SIMPLE:the first matched position is %d\n", pos0);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值