一直以来,对算法都是理论大于实际,甚至没有实际.
最近由于项目需要.从新了解了一下KMP算法.唉,讨厌这种被动的学习过程.
不过KMP算法还是很有意思的,用了两天的时间才总算是弄懂了.期间参考了网上的博文和数据结构.下面分享一下KMP算法的心得.
KMP的总体思想是利用模式串本身的特性来优化匹配的步骤.如何利用自身的特性呢,KMP借助一个数组来实现,也就是大多数教程中提到的next数组.后面我会介绍next数组是如何构建和使用的.
前面提到KMP算法需要模式串满足一定的条件,那么这个条件是什么呢.这里直接引用数据结构书中的等式:
当k < j 时, t1t2…tk-1tk = tj-(k-1)tj-k… tj-1 tj.
如果没有这个等式,那么KMP算法无异于浪费了next大小的空间的最普通的字符串匹配算法.
下面用一张图来描述一下这个等式在匹配子串的时候起到的作用:
(本图片假定数组下标从1开始)首先我们找到等式: t1t2 = t6t7.所以当t8不匹配的时候,KMP算法会自动对齐t1 t2 然后用t3和母串进行匹配.
那么根据以上的分析,我们可以得出以下的几个结论(可能得出的有点仓促,同学们还是需要结合课本来进行理解)
1. next数组和模式串的下标是一一对应的.
2. 每个字符对应的next保存的是其前一个字符匹配的前缀的下标.从上面的图中可以看到next[8]保存的是3.
3. 第一个字母没有前缀,所以它的next保存的是一个无效的下标.在实际编程中,可以是-1
4. 如果一个字符的前面没有满足等式的子串,则其next保存的是模式串的首字母的下标.
根据我们的结论,下面给出KMP算法的实现:
void kmp_get_next(char *pattern, int next[])
{
int i = 0, j = 0;
next[i] = -1;
while (i < strlen(pattern) - 1)
{
if (j == i || pattern[i] == pattern[j])
{
if (j != i)
{
j++;
}
i++;
next[i] = j;
}
else if (j != 0)
{
j = next[j];
}
else
{
i++;
}
}
}
void kmp_print_next(char *pattern, int next[])
{
int i = 0;
if (NULL == pattern)
{
return;
}
printf("%s 's next array is : \n", pattern);
for (i = 0; i < strlen(pattern); i++)
{
printf("%d\t", next[i]);
}
}
int kmp_is_match(char *pattern, char *basestr, int next[])
{
int i = 0;
int j = 0;
while (i < strlen(basestr))
{
if (basestr[i] == basestr[j])
{
if (j == strlen(pattern))
{
return 0;
}
i++;
j++;
}
else
{
j = next[j];
if (j == -1)
{
i++;
}
}
}
return -1;
}
int kmp_get_match(char *pattern, char *basestr, int next[])
{
int i = 0;
int j = 0;
while (i < strlen(basestr))
{
if (basestr[i] == basestr[j])
{
if (j == strlen(pattern))
{
return i;
}
i++;
j++;
}
else
{
j = next[j];
if (j == -1)
{
i++;
}
}
}
return -1;
}
#include "kmp.h"
int main(int argc, char **argv)
{
int ret = 0;
int i = 0;
char *pattern = "abaabc";
int next[10] = {0};
char *basestr = "abcabcabcabccacabdabaabcabceadb";
kmp_get_next(pattern, next);
printf("%s matched\n", kmp_is_match(pattern, basestr, next) == 0 ? "is" : "not");
ret = kmp_get_match(pattern, basestr, next);
return 0;
}
KMP算法其实理解起来并不难,比较难的就是构造next数组.至少对我而言是这样.一旦数组构造出来,那剩下的就都好办了.
可能是我理解的还不够深把,写之前觉得有很多内容可以分享,但是开始写以后发现不知道该写什么.希望能够对同学们有所启发.