一.算法功能:
改进的模式算法是由Knuth,Morris和Pratt等人共同提出的,所以称为Knuth-Morris-Pratt算法,简称KMP算法。KMP算法是字符串模式匹配中的经典算法,在起匹配过程中,主串指针不回溯,从而提高了算法性能。
二.算法思想
1. 在匹配过程中,如果出现不匹配的情况(当前模式串不匹配字符假设为t[i]),首先从已匹配结果计算出目标串S的第i个字符应该与模式串T中哪个字符在比较时出现了不配的情况,即保证在目标串指针不回溯的前提下,确定模式串中新的比较起点。
设主串是S=’S1S2.....Sn',模式串为T=T1T2.....Tm;
2.根据模式串T自身的规律和已知当前的位置j,可以归纳出计算模式串新的比较起点k的表达式。令k=next[j];
next[j]=0 (当j=1)
next[j]=max{k|1<k<j且'T1T2.....Tk-1'='Tj-k+1Tj-k+2......Tj-1';
next[j]=1; 其他情况
需要说明的是,next[j]中next[0]和next[1]的取值是固定的。
*匹配规则:*
(1)当首字母出现不匹配的时候,目标串的指针后移一位,然后在从改位与模式串的第一个字符开始匹配。假定next[0]=-1;
(2)失配位置j所对应的next[j]的值为接下来要匹配的模式串的字符的索引,也就是说,出现不匹配的时候,模式串的索引指针要回溯到next[j]所对应的位置,而目标串索引指针不回溯。
3.举个例子说明:
例如,现有目标串S='cabdabaabcabaabadcb',模式串T='abaaba'
1).先把模式串T中可能的失配点j多对应的next[j]计算出来(*由上面的next[j]计算公式*)
j=1时,next[1]=0;
j=2时,next[2]=1;
j=3时,首先,1<k<j,所以k=2; 但是'T1'(此处T1为a)不等于'T2'(此处T2为b),所以next[3]=1;
j=4时,k={2,3},k=2时,'T1'='T3'(此处T1等于a,T2等于a)
k=3时, 'T1T2'不等于'T2T3'(此处T1T2是ab,T2T3是ba)
因此next[4]=max{k|1<k<j且 'T1T2.....Tk-1'='Tj-k+1Tj-k+2......Tj-1'}=2;
以此类推
j=5时,next[5]=2;
j=6时,next[6]=3;
2).计算完next[j]的值,接下来开始匹配
*第一次匹配*:S='*c*abdabaabcabaabadcb'
T='*a*baaba'
设两个参数i和j,i代表目标串的指针索引位置,j代表模式串指针索引位置,开始时,i=1,j=1; 第一个字符都不匹配,next[j]=next[1]=0;
*第二趟匹配*:目标串指针加1,i=2,j=1;
S='c*abd*abaabcabaabadcb'
T='*aba*aba'
匹配失败时,i=4,j=3;next[j]=1(当j=3时)
*第三趟匹配*:目标串指针不变,j=1.就是从i=4,j=1开始匹配(此处的原因是上文中的匹配规则)
S='cab*d*abaabcabaabadcb'
T='*a*baaba'
匹配失败时i=4,j=1,next[j]=0(当j=1时)
*第四次匹配*:目标串指针加1,模式串指针指向第一个字符,也就是j=1,从i=5,j=1开始匹配。
S='cabd*abaabc*abaabadcb'
T='*abaaba*'
失配时i=10,j=6,next[j]=3(j=6)
*第五次匹配*:目标串不变,从i=10,j=3,开始匹配,
S='cabdabaab*c*abaabadcb'
T='ab*a*aba'
失配时,i=10,j=3,next[j]=1(j=3)
*第六次匹配*:目标指针不变,从i=10,j=1,开始匹配,
S='cabdabaab*c*abaabadcb'
T='*a*baaba'
失配时i=10,j=1,next[j]=0;
*第七次匹配*:目标指针加1,j=1,从i=11,j=1开始匹配
S='cabdabaabc*abaaba*dcb'
T='*abaaba*' 匹配成功,返回模式串在目标串中的位置11.
以上匹配过程看起来挺麻烦的,只要一步步慢慢来,是很容易理解的;
三.下面是具体的代码实现:
#include<stdio.h>
#define MAXL 255
#define OK 1
#define OVERFLOW -1
typedef unsigned char SString[MAXL + 1];
void strAssign(SString &T, char *s)
//用字符数组s给串T赋值.
{
int i = 0;
T[0] = 0;//0号单元存储串长.
for (; s[i]; i++)
{
T[i + 1] = s[i];
}
T[0] = i;
}
void get_next(SString T, int next[])
{
//求模式串T的next函数值并存入数组next中
int i = 1, j = 0;
next[1] = 0;
while (i < T[0])
{
if (j == 0 || T[i] == T[j])
{
i++; j++;
next[i] = j;
}
else
j = next[j];
}
}
int Index_KMP(SString S, SString T)
{
//求子串T在主串S中可以匹配的位置,不匹配返回0
int i = 1, j = 1;
int next[MAXL];
get_next(T, next);
while (i <= S[0] && j <= T[0])
{
if (j == 0 || S[i] == T[j])
{
i++;
j++;
}
else
j = next[j];
}
if (j > T[0]) return i - T[0];
else return 0;
}
void main()
{
int pos;
SString T, S;
char char_a[100], char_b[100];
printf("请输入主串A:");
gets_s(char_a);
printf("%s\n", char_a);
printf("请输入主串B:");
gets_s(char_b);
printf("%s\n", char_b);
strAssign(T, char_a);
strAssign(S, char_b);
printf("赋值成功!\n");
pos = Index_KMP(T, S);
if (pos)
{
printf("主串 T=%s 的子串 S=%s 在第%d个位置开始匹配。", char_a, char_b, pos);
}
else
printf("主串 T=%s 和子串 S=%s 不匹配", char_a, char_b);
}