本文主要通过C语言实现KMP算法改进版并讲其实现原理
KMP算法简介
KMP是一种改进的字符串匹配算法由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,相比较传统的依次进行的字符匹配做出了相应的改进如在字符串S="dddsafdsf"中匹配字符串T="dds"传统方法:从S的第一个字符"d"开始匹配显然s的第一个字符等于T的第一个字符,所以将S的第二个字符与T的第二个字符相比较依然相等再将S的第三个字符与T的第三个字符相比较发现并不相等,于是又要从S的第二个字符开始匹配T的第一个字符,而使用KMP算法不仅可以少了这一个回溯的操作直接从S的第三个字符串开始匹配并且还可以直接将S的第三个字符与T的第二个字符相比较(因为S中的第二个字符与T中的第一个字符相等所以直接从S的第三个字符与T的第二个字符开始比较)显而易见KMP算法比普通的字符串匹配算法效率高了许多。
标记
通过要匹配的字符串T的前后缀重复程度给每个字符打上标记第n个字符的标记为next[n]如字符串"abcdabc"的标记值依次分别为-1,-1,-1,-1,-1,0,1标记的规则是前两个next的值均为-1,从第三个起时next的值便为要匹配的字符串的第前n-1个字符的前后缀对称个数(前缀:包含字符串第一个字符不包含最后一个字符例如字符串"safs"的前缀有"s",“sa”,“saf”,后缀:包含字符串最后一个字符不包含字符串第一个字符以前面的例子为例后缀有"s",“fs”,“afs”.前后缀中相等的字符串里的最长的字符串中有几个字符next的值便由-1加几).通过这个标记有助于让我们在字符串的匹配过程中被匹配的字符串S不用回溯大大的提高了效率。
对此我们还是不够满意因为当有多个重复的字符时往往T字符串要进行多次回溯如当字符串S为"aaaaccaaaa"字符串T为"aaaab"时如果进行匹配时T字符串就会做许多无用的回溯为了避免这种情况我们便对回溯时相同的字符赋同样的next值使它回溯时不判断同样的字符
给T字符串做标记的C语言代码如下:
int *get_next(int length, char *str1, int *next)
{
int i,j;
i = 0;
j = -1;
next[0] = -1;
while (i < length) {
if (j == -1 || str1[i] == str1[j]) {
++i;
++j;
if (str1[i] != str1[j])
next[i] = j;
else
next[i] = next[j];
} else
j = next[j];
}
return next;
}
通过标记实现
通过标记进行比较是如果不相同两个字符不相同是j值便进行回溯如果相同则开始下一轮比较
int Index_KMP (char *str, char *str1, int length, int length_main, int pos)
{
int i = pos;
int j = 0;
int next[100];
get_next(length, str1, next);
while (i <= length_main && j <= length){
if (j == -1 || str[i] == str1[j])
{
i++;
j++;
}else{
j = next[j];
}
}
if (j > length)
return i - length;
else
return -1;
}
具体实现
#include "stdio.h"
#include "string.h"
int Index_KMP (char *str, char *str1, int length, int length_main, int pos);
int main()
{
int length;
int length_main;
char str1[20];
char str[20];
printf("请输入待匹配字符串:");
scanf("%s", str);
printf("请输入你要匹配的字符:");
scanf("%s", str1);
length = strlen(str1);
length_main = strlen(str);
printf("%d", Index_KMP(str, str1, length, length_main, 1));
}
int *get_next(int length, char *str1, int *next)
{
int i,j;
i = 0;
j = -1;
next[0] = -1;
while (i < length) {
if (j == -1 || str1[i] == str1[j]) {
++i;
++j;
if (str1[i] != str1[j])
next[i] = j;
else
next[i] = next[j];
} else
j = next[j];
}
return next;
}
int Index_KMP (char *str, char *str1, int length, int length_main, int pos)
{
int i = pos;
int j = 0;
int next[100];
get_next(length, str1, next);
while (i <= length_main && j <= length){
if (j == -1 || str[i] == str1[j])
{
i++;
j++;
}else{
j = next[j];
}
}
//返回值为第一次匹配到该字符串的位置
if (j > length)
return i - length;
else
return -1;
}