KMP算法

怎么说呢,KMP算法确实有难度。我一开始就是搞不明白,半懂不懂的,后来看了大神的帖子才有点小明白。特此写个博客,加深一下印象,并且希望能够理解的更透彻一些。

大神博客:http://www.cnblogs.com/tangzhengyue/p/4315393.html

要了解KMP算法,首先需要了解BF算法。

一、BF算法——最简单最直观的模式匹配算法

首先,要说明的是模式匹配不一定是从主串的第一个位置开始的,所以要指定主串查找的其实位置pos.下面是以顺序结构存储的字符串为例介绍。

算法步骤:

1、分别利用指针i,j指示主串S和模式串T中当前正待比较的的字符位置,i初值为pos,j初值为1.(字符串不用0位置)

2、如果两个串都没有比较到各自的最后(就是指针i,j分别小于等于S和T字符串的长度),则循环执行以下操作:

     (1)两个字符串当前位置(S.ch[i]和T.ch[j])若果相等,那么就各自向后移一位,继续比较。

      (2)两个字符串当前位置(S.ch[i]和T.ch[j])若果不相等,那么就需要回溯,显而易见模式串需要回到1(j=1),而主串则需要                   回溯到上一次开始匹配的下一个位置(i=i-j+2)。

3、跳出循环后,有两种可能方式跳出循环,所以需要判断是那种方式跳出循环的来判定字符串是否匹配成功。若j当前指向的位置大于模式串的长度(j>T.length),则说明是模式串匹配成功了,那么返回和模式串T中第一个字符相等的字符在主串S中的序号(i-T.length);否则就是主串超过了末尾跳出的循环,说明匹配不成功,返回0.

 

上面算法虽然写的很多,但是总的来说思路很简单,主串每一个字符都和模式串的第一个字符开始向后比较,直到寻找到匹配或者主串遍历完之后匹配失败。

代码:

#include<stdio.h>
#define MAXSIZE 255
typedef struct{
    char ch[MAXSIZE];
    int length;
}SString;

int Index_BF(SString S,SString T,int pos);
int main(){
    SString S,T;
    S.length=0;
    T.length=0;
    char c;
    for(int i=1;i<255;i++){
        scanf("%c",&c);
        if(c=='\n')
            break;
        S.ch[i]=c;
        S.length++;
    }
    for(int i=1;i<255;i++){
        scanf("%c",&c);
        if(c=='\n')
            break;
        T.ch[i]=c;
        T.length++;
    }
   int i= Index_BF(S,T,1);
    printf("%d",i);
    return 0;
    
}
int Index_BF(SString S,SString T,int pos){
    int j=1;
    int i=pos;
    while(i<=S.length&&j<=T.length){
        if(S.ch[i]==T.ch[j]){
            i++;
            j++;
        }
        else{
            i=i-j+2;
            j=1;
        }
    }
    if(j>T.length){
        printf("OK");
        return i-T.length;
    }
    else{
        printf("NO");
        return 0;
    }
}

算法部分:

int Index_BF(SString S,SString T,int pos){
    int j=1;
    int i=pos;   //初始化i,j.
    while(i<=S.length&&j<=T.length){   //如果两个指针都小于各自的长度,则进入循环
        if(S.ch[i]==T.ch[j]){//若当前位置相等就各自向后移动一位
            i++;
            j++;
        }
        else{         //若不相等则回溯
            i=i-j+2;    //先将主串回溯的本次开始匹配的下一个位置
            j=1;   模式串回到1
        }
    }
    if(j>T.length){   //判断,如果模式串指针大于字符数组的长度那么就说明匹配成功
        printf("OK");
        return i-T.length;   //返回位置
    }
    else{
        printf("NO");
        return 0;
    }
}

 

二、KMP算法——让人刚开始学脑子嗡嗡的算法(大神请忽略 )

KMP 算法是在BF算法的基础上改进来的,它可以在O(n+m)的时间数量级上完成串的匹配。

他与BF算法的不同之处就是主串不用回溯。

它的思想刚开始时和BF算法一样,如果两个指针都小于数组长度,那么就进入循环,如果当前位置相等,都想后移一位;如果不等,主串不动,模式串滑动到该位置对应的next[j]位置,继续进行比较。

它的思想是:

记录先模式串当前位置的前缀字符串和后缀字符串的重复情况,并且记录重复的前缀字符串的最后一个字符的位置K,next[j]=k。

这样做当当前位置S.ch[i]不等于T.ch[j](就是书上的失配)时,就说明当前位置前面的已经匹配了,那么当前位置的前缀字符串和后缀字符串相当,就可以把模式串向后滑动,j=next[j],这样前缀字符串和滑动到后缀字符串的位置,继续进行比较。

那么问题又来了,如果j=next[j]之后进行比较S.ch[i]仍不等于T.ch[j]怎么办,那就继续让j=next[j]。这里是最难理解的。

 

由此可见,KMP算法与主串关系不大,最主要的是模式串的next数组,下面就着重解析一下如何求next数组:

 

下面借助大神的图进行分析

1.由"next[j] == k;"这个条件,我们可以得到A1子串 == A2子串(根据next数组的定义,前后缀那个)。

2.由"next[k] == 绿色色块所在的索引;"这个条件,我们可以得到B1子串 == B2子串

3.由"next[绿色色块所在的索引] == 黄色色块所在的索引;"这个条件,我们可以得到C1子串 == C2子串

4.由1和2(A1 == A2,B1 == B2)可以得到B1 == B2 == B3

5.由2和3(B1 == B2, C1 == C2)可以得到C1 == C2 == C3

6.B2 == B3可以得到C3 == C4 == C1 == C2

由上面各种推理可以知道,2个A串相等,而A串里的B1和B2相等,所以A1里的B1也等于2里的B3.B1里面有C1和C2,那么B1里面的C1也一定等于B3里面的C4.

总结来说就是当前前缀字符串里面的前缀字符串,一定与当前后缀字符串的后缀字符串相等。

所以这样当仍然不匹配的时候,就可以继续让j=next[j],从下一个前缀入手。

 

 

由next的得:next[1]=0;

next[j]=k,说明前面有k-1个前缀字符串(T1~Tk-1)和K-1个后缀字符串(Tj-k+1~Tj-1)相等。

那么如何得到j+1的位置next[j+1]的值呢?

由上面的next[j]=k可以直到,当前j位置记录的是他前面前缀和后缀的相等情况,和j本身无关,所以得到next[j+1]的值是有前面字符串决定的。

如果ch[j]=k(即ch[j]=next[j],一位next[j]已经在j-1时确定了),那么next[j+1]=k+1(即next[j+1]=next[j]+1).意思就是如果j位置等于next[j]位置(next[j]记录的是前缀字符串的下一位),那么j+1的前缀就是k个字符了,也就是说next[j+1]=k+1.

如果ch[j]!=k,那么只能从前缀的前缀下手了,k=next[k],然后继续比较ch[i]与k.

代码:

void get_next(SString T,int next[]){
    next[1]=0;
    int i=1;
    int j=0;
    while(i<T.length){
        if(j==0||T.ch[i]==T.ch[j]){
            next[++i]=++j;
        }
        else
            j=next[j];
    }
}

KMP代码:

int Index_KMP(SString S,SString T,int pos,int next[]){
    int i=pos;
    int j=1;
    while(i<=S.length&&j<=T.length){
        if(j==0||S.ch[i]==T.ch[j]){
            ++i;
            ++j;
        }
        else
            j=next[j];
    }
    if(j>T.length){
        printf("OK");
        return i-T.length;
    }
    else{
        printf("NO");
        return 0;
    }
}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值