KMP浅谈

分析

设主串(下文中我们称作T)为:a b a c a a b a c a b a c a b a a b b

模式串(下文中我们称作W)为:a b a c a b

用暴力算法匹配字符串过程中,我们会把T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。

而在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。

比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。

在第一次匹配过程中

T: a b a c a a b a c a b a c a b a a b b

W: a b a c a b

在T[5]与W[5]出现了不匹配,而T[0]~T[4]是匹配的,现在T[0]~T[4]就是上文中说的已经匹配的模式串子串,现在移动找出最长的相同的前缀和后缀并使他们重叠:

T: a b a c aa b a c a b a c a b a a b b

W: a b a c a b

然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。

然而,如果每次都要计算最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。

NEXT数组:

         两张图:

模板:

#include 
#include 
#include 
#include 
#include 
#include 
#include #include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
typedef long long ll;
const int maxn = 1000000+5;
int next[maxn];
char s[maxn],a[maxn];
void cal_next(char *a){
    memset(next,-1,sizeof(next));
    int len = strlen(a);
    int k = -1 ;
    next[0]=-1;
    for(int p = 1 ; p < len ; p++){
        while(k>-1&&a[p]!=a[k+1]) k = next[k];
        if(a[k+1]==a[p]) next[p]=++k;
    }
}
int kmp(char *s,char *a){
    int slen = strlen(s);
    int alen = strlen(a);
    int k = -1;
    for(int i = 0 ; i < slen ; i++){
        while(k>-1&&a[k+1]!=s[i]) k = next[k];
        if(a[k+1]==s[i]) k++;
        if(k == alen - 1){
            return i;
        }
    }
    return 0;
}
int main(){
    int kase;
    scanf("%d",&kase);
    while(kase--){
        scanf("%s",a);
        scanf("%s",s);
        cal_next(a);
        printf("%d\n",kmp(s,a));
    }
}

其他的回来补坑。。。

你可能感兴趣的:(kmp)