当我们实现在文本中寻找是否有特定的文本出现,我们需要实现文本匹配算法。
比如在文本:aabaabaaf 中,我们要查询是否有文本:aabaaf出现。
关于暴力求解,我就不再过多赘述,直接进入正题。
kmp算法的关键——最长公共前后缀
关于前后缀不了解的同学,我会简略说明。
前后缀:
前缀:是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀:是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
如aabaa:
前缀:a,aa,aab,aaba(从前向后)
后缀:a, aa , baa , abaa(从后向前)
最长公共前后缀(next数组的关键)
我们使用上文的列子 eg:
下标 012345
文本 aabaaf
前缀表 010120
//前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。
对于a,它是没有最长公共前后缀的。
对于aa,前缀是a,后缀是a,前后缀有一个相同,这值为1
对于aab,前缀是aa,后缀是ab,不相同,值为0;
对于aaba,前缀是aab,后缀是aba,只有第一个相同,值为1;
对于aabaa,前缀是aaba,后缀是 abaa,最长相等(aa)为2个,值为2。
因此我们得到了此文本的前缀表。
next数组代码实现
//前缀表的实现
//T字符串是所需要查询的文本,len是字符串大小
void getNext(const char* T, int* next,int len)
{
int j = 0;
next[0] = 0;
//第一个的前缀表一定是零,故初始化next[0],并跳过第一个
//i=1
for (int i = 1; i < len; i++)
{
//前后缀不相同的时候
while (j > 0 && T[i] != T[j])
{
j = next[j - 1];//当不相等时,j回退,如果回退后还不相 //等,继续回退,直到j==0。
}
//相同的时候
if (T[i] == T[j])
{
j++;
}
//将j(前缀的长度)赋给next[i]
next[i] = j;
}
}
当我们有了前缀表后,就可以进行匹配了。
KMP实现:
int KMP(char* S, char* T, int* next, int Slen,int Tlen)
{
//j指向T,T是aabaaf。
int j = 0;
//i指向S文本,S是aabaabaaf。
for (int i = 0; i < Slen; i++)
{
if (S[i] = T[j])
{
j++;
}
else {
while (j > 0 && S[i] != T[j])
{
j = next[j - 1];//退回,直到S[i]==T[j]或者j==0
}
}
//匹配完成
if (j == Tlen - 1 )
{
return 1;
}
}
return -1;
}