理解KMP算法过程

  写下这篇文章,希望能够帮助新手快速入门,加深理解。废话不多说,进入正题。

  1.一些声明

  首先定义一些符号来简化文章

  Problem A 代表了 【判断一个字符串W是否在一个超大字符串T内出现过】 这个问题。

  LenW,LenT 分别代表W和T字符串的长度

   2.为什么选择KMP

  在算法竞赛中,传统的暴力匹配算法在面对【Problem A】时,n^2 的时间复杂度不能满足时间要求。因此我们需要一种能够 快速解决Problem A 的算法,而KMP算法恰好能够解决这个问题。

  3.什么是KMP

  KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。—---百度百科。

  4.KMP的具体过程

  以Problem A为例,我们假设给出的字符串

  W为:acbac 

  T为 :aababacbacdb 

  在暴力匹配时我们最坏情况下会将T的第(0~LenT-lenW)个字符依次作为起点,每次判断LenW次,这样的复杂度是LenW*LenT的。 但是,在KMP算法中,我们首先会对W字符串做一些O(LenW)操作,将每次判断次数都变为常数级别。

  具体操作就是,求一个Next数组,数组内的第i个数据代表从s[0]到s[i]的一个特殊子串的结束下标,这个特殊子串要满足三个条件:

  ①此子串是W的一个前缀 

  ②此子串是W的一个后缀 

  ③此子串尽量长 

  例如:aabca满足以上三个条件的子串是ac 

  求Next数组的具体过程O(LenW): 中间过程不给出,直接给出代码与注释,建议利用简单的串模拟一下中间过程,这样可以加深理解。

void Get_Next()
{
    int j = 0, k = -1;
    Next[0] = -1;
    while(j < len_w)
    {
        if(k == -1 || W[j] == W[k])
        {
            Next[++j] = ++k;
        }
        else
        {
            k = Next[k];
        }
    }
}

  得到Next数组后,匹配就很简单了,失配时直接向前找当前失配位置的Next,W串从Next数组保存的位置开始判断,这样在O(n+m)时间内就能解决Problem A

  

int Match()
{
    int sum = 0;
    getNext();
    for(int i = 0 , j = 0; i < LenT ; i++)
    {
        while(j > 0 && W[j] != T[i]) j = Next[j];
        if(W[j] ==  T[i]) j++;
        if(j == LenW)
        {
            sum++;
            j = Next[j];
        }
    }
    return sum;
}

猜你喜欢

转载自blog.csdn.net/zhaiqiming2010/article/details/79906970