kmp理解

来源:互联网 发布:淘宝支点运动是真的吗 编辑:程序博客网 时间:2024/05/01 18:33

kmp算法的核心就是对next 数组的理解和构造,,以前一直都理解错了于是一直背kmp代码模版[其实也是很好背的orz,,今天终于纠正过来。
首先对next[i]下一个定义。
如果子串第i位与母串第j位配失败了,则子串的第next[i]位 应该与母串第j位重新匹配。即,next[i]是子串与母串j重新匹配的位点。
而这个位点有什么特性呢?子串[0~next[i]-1]与子串[i-next[i]~i-1]是完全相同的。而这个相同的串,对于第i-1位来说是最长的。
因此,子串的[0~next[i]-1]位 与母串的[j-next[i]~j-1]是相同的。只需要从next[i]起继续与母串的第j位进行比较即可。不需要再从头开始比较,这样就节省了时间。

其次是next数组的构造问题。
它其实有一个继承的思想在里面,也就是我们说的递推。
对于sz[i] 我们想要找到 它的next[i], 可以直接继承next[i-1]里信息。举个栗子:比如此时的next[i-1]=5 意味着i-1已经和前 4 个字符相匹配了,它的前一位的最长前缀为4。
而这个时候我们想知道next[i]为多少,我们需要知道以sz[i -1]结尾的最长前缀为多少,因此我们就需要比较sz[next[i-1]]与sz[i-1]是否相同,如果相同则说明,以sz[i-1]结尾的最长前缀为5 所以next[i]==5+1(next[i-1]+1);
但如果不相同呢?这就相当于子串与母串匹配失败了一样,我们需要向前移动 找到一个最近的位点与sz[i-1]重新匹配,而这个位点正是存在next[next[i-1]]里的。于是就可以乳齿不断的往前递推。直到找到与sz[i-1]相等的那个位点next[p],或者找到了子串的起始点。
最后就可以得到以sz[i-1]为末位的最长前缀 而 next[i]=next[p]+1;

总而言之,之所以觉得绕是因为,next[i]始终表示的是 以sz[i-1]为末位的最长前缀的长度的下一位。。。。。

又一次觉得复杂度是一个谜。。。不要问窝为什么是O(m+n)

写的小作文,估计只有窝自己能看懂吧qwq。。。

下面是一道裸kmp。。。。但是在VJ上T了,HDU上A了。。。。。一定是窝写的太丑。。。。
HDU 1711:http://acm.hdu.edu.cn/showproblem.php?pid=1711

//  Created by ZYD in 2015.//  Copyright (c) 2015 ZYD. All rights reserved.//#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>#include <cstring>#include <climits>#include <string>#include <vector>#include <cmath>#include <stack>#include <queue>#include <set>#include <map>using namespace std;#define Size 100000#define ll long long#define mk make_pair#define pb push_back#define mem(array) memset(array,0,sizeof(array))typedef pair<int,int> P;int n,m,t,sm[1000005],sz[10005],nxt[10005];int kmp(){    int p=0;    nxt[1]=0;nxt[2]=1;    for(int i=3;i<=m;i++)    {        p=i-1;        while(p>1 && sz[nxt[p]]!=sz[i-1])            p=nxt[p];        nxt[i]=nxt[p]+1;    }    int p1=1;int p2=1;    while(p1<=n && p2<=m)    {        if(sm[p1]==sz[p2])        {            p1++;            p2++;            continue;        }        p2=nxt[p2];        if(p2<1)        {            p1++;            p2=1;        }    }    if(p2>m) cout<<p1-p2+1<<endl;    else cout<<-1<<endl;    return 0;}int main(){    freopen("in.txt","r",stdin);    cin>>t;    while(t--)    {        cin>>n>>m;        mem(sm);mem(sz);mem(nxt);        for(int i=1;i<=n;i++)            cin>>sm[i];        for(int i=1;i<=m;i++)            cin>>sz[i];        kmp();    }    return 0;}
0 0