大话kmp! (模版测试题hdu1171)

kmp是数据结构里面关于字符串很重要的内容
关于kmp,网上有很多资料,但是有的人看了看懂了,有的看了还是看不明白
所以我自己还是要自己整理一下。

关于kmp求next数组,这是最难的,很难理解,尤其是那一句k=next[k],真的是让人费解
首先,kmp高效的原因是因为,进行了预处理,然后匹配时主串不回溯,使得kmp效率达到了O(n+m),比起普通的暴力匹配高效了不少。

首先我们来分析一下get_next函数,这个是得到next数组的函数。
i的含义是求next[i+1]的值,j的含义是上一个next[]的值是多少,比如上一个j是5,然后这时t[i]==t[j],就是表示此时的next[i+1]==6,因为next数组表示的是最长前缀后缀

j==-1的时候,next[i]=0,这个不难理解。
t[i]==t[j]的时候,更好,直接+1了。
当t[i]==t[j]的时候,很有可能这个答案是0,也可能介于0~j-1之间了,反正不会超过j了,因为唯一一次等于j+1的机会已经错过了(t[i]==t[j]),于是我们该怎么选择,于是先辈们就说,选择k=next[k]这个位置!

为什么?
http://www.rudy-yuan.net/archives/182/
我也想讲,但是这篇博客讲的很清楚,我自己在记事本上的图画的太丑了,实在是不好看清楚,就直接看这篇博客把!

void get_Next(int m){
    next_[0]=-1;
    for(int i=0,j=-1;i<m;){
        if(j==-1||t[i]==t[j]){
            i++;j++;
            next_[i]=j;
        }
        else j=next_[j];
    }

    /*
    int j=0,k=-1;
    while(j<m){
        while(k>=0&&t[j]!=t[k]){
            k=next_[k];
        }
        j++;k++;
        next_[j]=k;
    }
    */
}

这里的get_next函数还有下面注释部分的写法,其实都是一样的。

int kmp_solve(int n,int m){
    get_Next(m);
    int i=0,j=0;
    while(i<n&&j<m){
        if(s[i]==t[j]){
            i++;j++;
        }
        else if(next_[j]==-1){
            i++;
        }
        else j=next_[j];
    }
    if(j>=m)return i-j+1;
    return -1;
}

可以看到
else if(next_[j]==-1)
这一句有人会发现和网上的代码不一样,当然在这里,很多人写的是j==0,这里写成这样我觉得更好理解。
其实都是一样的,根据我们求next的代码,计算next[i]时,j是++过的,这就意味着除了j==0之外,没有别的next值会是-1,但是我写成这样,可以理解为,我们下面要将next[j]得值赋值给j,所以我们排出j==-1的情况,以免运行错误。当然这只是一种理解,正确而且比较官方有信服力的解答是,这条语句,如果s[i]和t[0]不想等,那么i就可以直接++了,不用在调整j的位置了,因为对于i位置,t串已经不想等了,可以直接进行下一次匹配了。

写代码:(这个代码是hdu1171的代码,纯kmp模版)

#include<iostream>
using namespace std;
#include<cstring>
#include<cstdio>

const int maxn=1000005;
int s[maxn];
int t[maxn];
int next_[maxn];

void get_Next(int m){
    next_[0]=-1;
    for(int i=0,j=-1;i<m;){
        if(j==-1||t[i]==t[j]){
            i++;j++;
            next_[i]=j;
        }
        else j=next_[j];
    }

    /*
    int j=0,k=-1;
    while(j<m){
        while(k>=0&&t[j]!=t[k]){
            k=next_[k];
        }
        j++;k++;
        next_[j]=k;
    }
    */
}

int kmp_solve(int n,int m){
    get_Next(m);
    int i=0,j=0;
    while(i<n&&j<m){
        if(s[i]==t[j]){
            i++;j++;
        }
        else if(next_[j]==-1){
            i++;
        }
        else j=next_[j];
    }
    if(j>=m)return i-j+1;
    return -1;
}
int main(){
    int T;
    cin>>T;
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++){
            scanf("%d",&s[i]);
        }   
        for(int i=0;i<m;i++){
            scanf("%d",&t[i]);
        }
        int ans;
        if(n>=m)
             ans=kmp_solve(n,m);
        else ans=-1;
        cout<<ans<<endl;
    }
    return 0;
}

你可能感兴趣的:(大话kmp! (模版测试题hdu1171))