对于一个串中某个子串的定位操作称为串的模式匹配,其中待定的子串称为模式串。算法的基本思想:从主串的第一个位置起和模式串的第一个字符开始比较,如果想等,则继续逐一比较后续字符;否则从主串的第二个字符开始,在重新用上一步的方法与模式串中的字符比较,以此类推,知道比较完模式串的所有字符。若匹配成功,则返回模式串在主串中的位置;若匹配不成功,则返回一个可区别与主串所有位置的标记,如“-1”,这是最简单的模式匹配,暴力枚举。
上面是一个简单的模式匹配,优点是容易理解,实现简单;缺点也很明显:效率不高。
从简单模式匹配算法可以看两点重要信息:1.标记i在主串中的位置基本反映了算法完成的进度,i走的越快,或者说i回溯的距离越小,算法执行就越快。2.通过i和j的回溯,是的下一趟尽可能消除当前位置的不匹配,进而使i继续往前走。由此,容易看出提升效率的一个可能的突破点:不回溯i,想办法通过调整j的位置消除i的不匹配。
定义一个整型数组next[],next[n]中保存了模式串下标n处发生不匹配时,主串应当从模式串下标k处的字符开始比较,即next[n] = k;
由于最近比较忙,对于具体算法推导就先不写了,有空会补齐,整个推导的过程。
下面是KMP的一个简单实现,虽然求解next数组的方法不是一种高效的方法,但却更便于手工推导。
#include <stdio.h>
#include <string.h>
int KMP(char str[], char subStr[], int next[])
{
int i = 0, j = 0;
while(i < strlen(str) && j < strlen(subStr))
{
if(str[i] == subStr[j])
{
++i;
++j;
}
else
{
j = next[j];
if(j == -1)
{
j = 0;
++i;
}
}
}
if(j == strlen(subStr))
return i - strlen(subStr);
else
return -1;
}
void getNext(char subStr[], int next[])
{
int i = 0, j = -1;
next[0] = j;
while(i < strlen(subStr))
{
if(j == -1 || subStr[i] == subStr[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];
}
}
int main()
{
char str[100], subStr[50];
int next[50] = {0};
scanf("%s", str); ///主串
scanf("%s", subStr); ///模式串
getNext(subStr, next);
for(int i=0; i < strlen(subStr); i++) ///输出next数组的值
{
printf("%d ", next[i]);
}
printf("\n");
int pos = KMP(str, subStr, next);
printf("%d\n", pos);
return 0;
}