给小白看的KMP算法

浅谈KMP算法:

(大部分人的KMP写法都是不一样的)

 

一:

  先给大家推荐一个讲kmp比较好理解的一个博客:阮一峰

二:

 下面介绍一点相关概念:

栗子: 
  P串: ABCBD

  前缀:A,AB,ABC,ABCB,ABCBD 
  真前缀:A,AB,ABC,ABCB 
  后缀:D,BD,CBD,BCBD,ABCBD 
  真后缀:D,BD,CBD,BCBD

KMP算法里的next数组的含义:

栗子: 
  P串: ABCDABD 
  next[] = {-1, 0, 0, 0, 0, 1, 2, 0, }; 
  next[i] 的含义:P串前 i 个字符(包括第 i 个)的最长真前缀后缀公共长度;     
  如 i = 5时: 
    真前缀:A,AB,ABC,ABCD 
    真后缀:BCDA,CDA,DA,A

  显而易见, 前缀和后缀相同的只有 A,而 A 的长度为 1,所以next[5] = 1;

 

next数组求法:

//用通俗的语句说就是k是用来表示子串中前k个和后k个是相同的,i是用来遍历数组
void get_next(char *t,int lent){
    nex[0] = -1;
    for(int i = 0,k = -1;i < lent;){
        if(k==-1||t[i] == t[k]){
            ++k;++i;
            nex[i]=k;
        }else k = nex[k];
/*如果c[i]和c[k]中字符不同说明匹配是失败,要把k的值重新退到next[ k ]
直到两者相同才停止。这样做的好处是没必要再重新从头再来,节约时间*/
    }
}

 

简单KMP算法的实现:

//返回主串中匹配的位置(第一个),如果不匹配返回-1;
int kmp(char *s,char *t,int lens,int lent)
{
    int i = 0, j = 0;
    while(i < lens&&j<lent) {
        if(j==-1||s[i] == t[j]){
            i++;j++;
            if(j==lent){
                return i-j+1;
            }
        }else j=nex[j];
    }
    return -1;
}

 

几道例题:

洛谷P3375:

 

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e6 + 10;
int nex[maxn];
char s[maxn],t[maxn];
int lens,lent;
void get_next(){
    nex[0] = -1;
    for(int i = 0,k = -1;i < lent;){
        if(k==-1||t[i] == t[k]){
            ++k;++i;
            nex[i]=k;
        }else k = nex[k];
    }
}
    
void kmp()
{
    int i = 0, j = 0;
    while(i < lens&&j<lent) {
        if(j==-1||s[i] == t[j]){
            i++;j++;
            if(j==lent){
                printf("%d\n",i-j+1);
                j=nex[j];
            }
        }else j=nex[j];
    }
}
int main(){
    while(~scanf("%s %s",s,t)){
        lens=strlen(s);
        lent=strlen(t);
        get_next();
        kmp();
        for(int i=1;i<=lent;++i){
            printf("%d%c",nex[i],i==lent?'\n':' ');
        }
    }
    return 0;
}
View Code

 

posted @ 2018-04-23 20:05  Cwolf9  阅读(253)  评论(0编辑  收藏  举报