KMP算法学习

KMP这个算法原理的学习已经搁置了一年了,现在下定决心搞懂它。因为比较难,所以特地开辟几篇博客来,以后绝对不能只套板子,要深刻理解原理。

参考链接:http://blog.csdn.net/yutianzuijin/article/details/11954939/

首先,我们的目标是在O串中寻找f。

对于暴力匹配法,就是把f从O的开头开始匹配,遇到不匹配的就把f往后挪1位重新匹配。

而对于kmp算法,遇到失配的情况,不仅仅要把f往后挪1位,而是要把f往后挪很多位。不仅如此,还不用重新匹配,而是继续匹配。

下面这个图非常好,假设f串向后挪了k位。说明什么呢?说明A=B,A是f的前缀,B是失配位之前的后缀。

我们如果令next[i]表示f[0..i-1]最长的相等的前缀和后缀(串本身除外),那么每次向后移动的这个k就等于i-next[i]。

首先,这样挪之后,后面可以正常匹配是很显然的。那么问题是,这样挪,就不会漏掉中间的一些吗?被跳过的那些位就一定没有能匹配的吗?

答案是没有。可以用反证法来证明。假设有一个位置t它能匹配。类似于下面这个图。

我们发现,有一个更长的前缀和后缀相等了,与next[i]是最长的相等前缀后缀矛盾。因此中间漏掉的情况是不存在的。

所以我们可以放心大胆的每次失配,f串向后移动i-next[i]位,然后继续匹配失配位。

那么这里有一个问题,当匹配完整个f之后,下次该往哪走呢?(假如我们不仅仅满足于找到一个匹配位置,而是要找出所有匹配位置)

其实这里只需要想:匹配完一个串以后,下次可能再匹配的位置在哪里呢?其实把f往后挪l-next[l],其中l是f串的长度。

那么现在有一个问题了,上面说的把f往后挪i-next[i],实际上在编程实现的时候并不这样写。假设i是O的匹配指针,j是f的匹配指针,那么我们想把j往后挪j-next[j],其实就是j=next[j],下次继续让i和j匹配。

下面是假设我们已经得到了next数组,来求f串在O串中出现的次数和位置的代码。

int nxt[1000];
vector<int> P;
int KMP(char f[],int n,char O[],int m)
{
    int cnt=0;
    P.clear();
    getnext(f,n,nxt);
    int nowi=0,nowj=0;
    while (nowi<m)
    {
        while (nowj!=-1 && O[nowi]!=f[nowj]) nowj=nxt[nowj];
        nowi++,nowj++;
        if (nowj==n)
        {
            cnt++;
            P.push_back(nowi-nowj);
            nowj=nxt[nowj];
        }
    }
    return cnt;
}

下面是另一个重要问题,怎么求next数组。

void getnext(char x[],int m,int nxt[])
{
    int i,j;
    j=nxt[0]=-1;
    i=0;
    while(i<m)
    {
        while(-1!=j && x[i]!=x[j])j=nxt[j];
        nxt[++i]=++j;
    }
}
原文地址:https://www.cnblogs.com/acmsong/p/7265862.html