算法笔记 KMP算法 HERODING的算法之路
Posted HERODING23
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法笔记 KMP算法 HERODING的算法之路相关的知识,希望对你有一定的参考价值。
前言
使用暴力算法进行的字符串匹配操作往往不够高效,一般的时间效率为O(m*n),而KMP算法可以优化到O(m + n),这究竟怎么做到的呢?观察暴力匹配的方法,我们可以注意到匹配的指针会不断回退,这就导致了许多不必要的重复操作,而KMP在主字符串中指针不会回退,这就是高效所在之处。当然,一般情况下,普通模式匹配的实际执行时间复杂度近似O(m + n),因此至今仍被采用。
算法介绍
KMP算法的核心,是一个被称为部分匹配表(Partial Match Table)的数组,以及前缀和后缀。
比如对于字符串“abababca”,它的PMT如下表:
什么是字符串的前缀和后缀?如果字符串A和B,存在A=BS,其中S是任意的非空字符串,那就称B为A的前缀。例如,”Harry”的前缀包括{”H”, ”Ha”, ”Har”, ”Harr”},我们把所有前缀组成的集合,称为字符串的前缀集合。同样可以定义后缀A=SB, 其中S是任意的非空字符串,那就称B为A的后缀,例如,”Potter”的后缀包括{”otter”, ”tter”, ”ter”, ”er”, ”r”},然后把所有后缀组成的集合,称为字符串的后缀集合。要注意的是,字符串本身并不是自己的后缀。
所以,PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。
那么如何利用PMT实现加速字符串的查找呢?
如图 1.12 所示,要在主字符串"ababababca"中查找模式字符串"abababca"。如果在 j 处字符不匹配,那么由于前边所说的模式字符串 PMT 的性质,主字符串中 i 指针之前的 PMT[j −1] 位就一定与模式字符串的第 0 位至第 PMT[j−1] 位是相同的。这是因为主字符串在 i 位失配,也就意味着主字符串从 i−j 到 i 这一段是与模式字符串的 0 到 j 这一段是完全相同的。而我们上面也解释了,模式字符串从 0 到 j−1 ,在这个例子中就是”ababab”,其前缀集合与后缀集合的交集的最长元素为”abab”, 长度为4。所以就可以断言,主字符串中i指针之前的 4 位一定与模式字符串的第0位至第 4 位是相同的,即长度为 4 的后缀与前缀相同。这样一来,我们就可以将这些字符段的比较省略掉。具体的做法是,保持i指针不动,然后将j指针指向模式字符串的PMT[j −1]位即可。
简言之,以图中的例子来说,在 i 处失配,那么主字符串和模式字符串的前边6位就是相同的。又因为模式字符串的前6位,它的前4位前缀和后4位后缀是相同的,所以我们推知主字符串i之前的4位和模式字符串开头的4位是相同的。就是图中的灰色部分。那这部分就不用再比较了。
有了上面的思路,我们就可以使用PMT加速字符串的查找了。我们看到如果是在 j 位 失配,那么影响 j 指针回溯的位置的其实是第 j −1 位的 PMT 值,所以为了编程的方便, 我们不直接使用PMT数组,而是将PMT数组向后偏移一位。我们把新得到的这个数组称为next数组。下面给出根据next数组进行字符串匹配加速的字符串匹配程序。其中要注意的一个技巧是,在把PMT进行向右偏移时,第0位的值,我们将其设成了-1,这只是为了编程的方便,并没有其他的意义。在本节的例子中,next数组如下表所示。
代码实现
那么,求next数组值的程序如下:
void getNext(char * p, int * next)
{
next[0] = -1;
int i = 0, j = -1;
while (i < strlen(p))
{
if (j == -1 || p[i] == p[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];
}
}
KMP实现的代码如下:
int KMP(char * t, char * p)
{
int i = 0;
int j = 0;
while (i < strlen(t) && j < strlen(p))
{
if (j == -1 || t[i] == p[j])
{
i++;
j++;
}
else
j = next[j];
}
if (j == strlen(p))
return i - j;
else
return -1;
}
本文参考链接:https://www.zhihu.com/question/21923021
以上是关于算法笔记 KMP算法 HERODING的算法之路的主要内容,如果未能解决你的问题,请参考以下文章
算法笔记 万物皆可DP——动态规划常见类型 HERODING的算法之路
算法笔记 揭开广度优先遍历BFS的神秘面纱 HERODING的算法之路
算法笔记 C++中const和auto的那些事 HERODING的算法之路