您的位置:首页 > 其它

[置顶] KMP算法详解【ORZ式教学】

2017-08-18 19:27 344 查看

引入篇

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数组篇

下面将以一个典型的例子来介绍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匹配过程:



郑重声明一下,图片从SYCstudio转载来的,特别感谢。

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;
}


KMP应用

字符串匹配问题。学习AC自动机也需要掌握的算法。等等……

练习题:

POJ_1961

POJ_3461

POJ_2752

POJ_2406

HDU_2087

HDU_1686

HDU_2203

HDU_1358

HDU_1711

HDU_1238

参考博客

温姑娘的博客

SYCstudio
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: