KMP算法详解【ORZ式教学】

引入篇

KMP算法是一个效率非常高的字符串匹配算法;是一种在线性时间内能处理两个字符串的包含关系的算法。那有没有效率更高的呢?当然是有的,就是Boyer-Moore算法。这里就只讲述一下KMP。
举例说明该算法的核心:求一个字符串里有没有另一个字符串返回其下标;一个字符串里有几个另一个字符串。
问A中是否存在B:
A=”abcaabababaa”
B=”abab”
常规的方法就是暴力进行比较,如下图:
这里写图片描述

但这样的是非常耗时的,时间复杂度达到了O(n∗m)。KMP算法的复杂的可以优化到O(n+m)
我们既然已经知道了在这一步骤中不匹配
这里写图片描述

但知道向后移一步是不成立的。a和bc都不会匹配,还要继续向后移动。所以我们就需要对这一点进行进一步的优化,让我们的算法可以把根本不匹配的部分直接跳过去。这就是下面要讲的KMP算法。

算法篇

  • 首先需要知道前缀和后缀的概念
    前缀:指的是字符串的子串中从原串最前面开始的子串,不会加入最后的字符,如abab的前缀有:a,ab,aba
    后缀:指的是字符串的子串中在原串结尾处结尾的子串,不会加入第一个字符,如abab的后缀有:b,ab,bab
  • 接着是next数组:next[i]的值就是i下标之前的字符串的最长公共前缀和后缀的长度。如abab的next[4],看上面前缀后缀的部分可以知道,公共的前缀后缀为ab,则next[4]=2。
  • 最后就是通过next数组进行KMP匹配算法。

next数组篇

A=”abaabaabbabaaabaabbabaab” 
B=”abaabbabaab”

其next数组的值为:
这里写图片描述

注意: next数组下标从1开始,0的位置为-1,目的是方便判越界。
next数组代码

void GetNext(string B)
{
    int len_B = B.size();
    nex[0] = -1;
    int k = -1,j = 0 ;
    while(j < len_B)
    {
        if(k == -1 || B[j] == B[k])
        {
            ++ k; ++ j;
            nex[j] = k;
        }
        else
            k = nex[k];
    }
}

代码部分较为难懂,可以慢慢理解。但最少需要明白他的含义。

KMP函数

还是这个例子:

A=”abaabaabbabaaabaabbabaab” 
B=”abaabbabaab”
  • 首先我们先规定i为A数组的下标,j为B数组的下标。i,j两指针同时向前匹配。为了方便,我们都认为字符串从1开始匹配,在实际的算法当中我们都是从0开始的。

    这里写图片描述

  • 可以在i=6,j=6的位置出现不匹配,这时候找nex[j−1]的位置,值为2,直接可以将B数组指针j拉到nex[j−1]+1的位置。这时候i=6,j=3。

    这里写图片描述

  • 好下面就接着向后面匹配

这里写图片描述

  • 这时候在i=14,j=11的位置出现不匹配,同上,找到nex[j−1]值为4,j=nex[j−1]+1。这时候i=14,j=5。

这里写图片描述

  • 接着匹配

这里写图片描述

i=14,j=5 出现不匹配,j 变成 2接着向后匹配

这里写图片描述

  • 接着就是又撞南墙

    这里写图片描述

  • i=14,j变成1开始匹配。

这里写图片描述

  • 最后配对成功。

附上完整的KMP匹配过程:

这里写图片描述

KMP函数代码

int kmp(string A, string B)
{
    int ans = -1, i = 0, j = 0;
    int len_B = B.size(), n = A.size();
    while(i<n)
    {
        if(j==-1||A[i] == B[j])
        {
            ++i;
            ++j;
        }
        else
            j = nex[j];
        if(j == len_B)
        {
            return i-len_B + 1; //A数组从0开始所以应返回的位置+1.
        }
    }
    return ans;
}
posted @ 2017-11-22 10:44  南风古  阅读(318)  评论(0编辑  收藏  举报