什么是KMP算法呢?
KMP算法是一种改进的字符串匹配算法,其核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。
KMP算法的时间复杂度O(m+n)。
如下图所示,假设有两个字符串,假设将字符串Ar称之为主串,将字符串称为模式串,现在使用一个算法去判断模式串是否与主串相匹配,或者是主串的一部分。
当使用普通的算法去判断两个字符串是否匹配时,我们会把Ar[0] 跟 Br[0] 匹配,如果相同则匹配下一个字符,出现不相同的情况,我们会丢弃前面的匹配信息,然后把Ar[1] 跟 Br[0] 匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了算法的匹配效率。
在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配,右移的距离是这样计算的:在匹配的模式串子串中找出最长长度的相同前缀和后缀,然后进行移动,使他们重叠。
在这里说一下什么是前缀和后缀。举个例子会清晰的表示前缀和后缀。假如有一个字符串为“hello”,则此字符串的前缀包括“h”、“he”、“hel”、“hell”,我们将前缀所组成的集合称之为前缀集合(字符串本身不是自己的前缀,也不是自己的后缀)。此字符串的后缀包括“o”、“lo”、“llo”、“ello”,我们将后缀组成的集合称之为后缀集合。
再比如有一个字符串“abcdabc”,它的前缀集合为{“a”、“ab”、“abc”、“abcd”、“abcda”、“abcdab”},其后缀集合为{“c”、“bc”、“abc”、“dabc”、“cdabc”、“bcdabc”}。两个集合中重复的子串中长度最大的为“abc”。
普通算法代码如下:
#include<stdio.h>
#include<string.h>
#include<assert.h>
int simple(const char *str1, const char *str2)//n*m
{
assert(NULL != str1 && NULL != str2);
int sit = 0;
int i = sit;
int j = 0;
while (str1[sit] != '\0' && str1[i] != '\0' && str2[j] != '\0')
{
i = sit;
j = 0;
while (str1[i] != '\0' && str2[j] != '\0' && str1[i] == str2[j])
{
i++;
j++;
}
if (str2[j] == '\0')
{
return i - j;
}
if (str1[i] == '\0')
{
return -1;
}
sit++;
}
return -1;
}
int main()
{
char *str1 = "abcdabcabababcc"; //const char*
char *str2 = "abcc";
int sit = simple(str1, str2);
printf("%d\n", sit);
return 0;
}
代码运行结果如下图所示:
KMP算法代码如下:
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
int KMP(const char *str1, const char *str2)
{
assert(NULL != str1 && NULL != str2);
int *next = (int*)malloc(sizeof(int)*strlen(str2));
assert(NULL != next);
getNext(str2, next);
int i = 0;
int j = 0;
int len = strlen(str2);
while (str1[i] != '\0' && j < len)
{
if (j == -1 || str1[i] == str2[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == len)
{
return i - j;
}
return -1;
}
void getNext(const char *str, int *next)
{
assert(NULL != str && NULL != next);
//int len = strlen(str);
next[0] = -1;//因为搜索时不可以从0位置往前搜索了,即使没有找到。
int i = -1;
int j = 0;
while (j <= strlen(str))
{
if (i == -1 || str[i] == str[j])
{
i++;
j++;
next[j] = i;//next里面存放的是下一次搜索时的位置
}
else
{
i = next[i];
}
}
}
int main()
{
char *str1 = "abcdabcabababcc";//const char*
char *str2 = "abcc";
int sit2 = KMP(str1, str2);
printf("%d\n", sit2);
return 0;
}
代码运行结果如下图所示:
相对于普通的字符串匹配算法,KMP算法已经是非常优秀的了。