前言
记录贴膜小哥对KMP模式匹配算法的粗浅理解以及笔记
详细学习可以参考这篇–KMP不错的讲解。
引言
由于朴素模式匹配算法的低效O(n*m),所以有三位前辈研究出了O(n+m)的匹配算法–KMP
朴素算法的低效在于,匹配不成功时候,主串和子串都要进行回溯。KMP算法的优越性在于,主串不用回溯,且子串重复部分也不用回溯。
理解KMP算法的关键在于理解F数组:
1-什么是F数组
2-F数组如何生成
1-什么是F数组
举个栗子:两个串匹配时,E和F匹配失败,按照朴素匹配算法,主串后移一位,子串回溯重置。
其实子串BCDF与主串的BCD已经匹配成功,且BCD不同,这样其实子串B和主串的C,D再进行匹配是重复工作。
所以,可以直接跳到该步,相当于主串+n,子串置0
然而在现实生活中 BCDF这种串还是太少见,如果遇到BCBC这种重复的串就不能直接跳过了。
通过查找已批配的主串的后缀和子串的前缀相同最大长度作为子串的回溯位置。比如,BCB的前后缀最大长度为 B=1,所以子串从1的位置–C可是匹配。
所以F数组是什么呢?F数组存放失败位置之前的串的最大前后缀长度。用来控制子串回溯的位置。子串不重复,完全回溯,子串重复,回溯除了重复的部分。重复也就是最大重复,最大重复就是前后缀最大长度。
2-F数组如何生成
F数组的生成,本质上是子串自己匹配自己
再举个栗子:
一个子串,最大的前后缀长度,F[I]和F[I-1]是有关系的。匹配成功时,F[I]=F[I-1]+1。就如 已知 "ABC …AB"串的最大长度为2 ,串“ABC…ABC”,只要判断C==C则最大长度为2+1.若串"ABC…ABA"不相等,则迭代回溯,即回溯F[F[2]]即判断B是否等于A,失败,再回溯到A,F[0],判断是否等于A,相等,所以"ABC…ABA"的F数组为0+1=1
i=0 | i=1 | i=2 | i=3 | i=4 | F[0] | F[1] | F[2] | F[3] | F[4] |
---|---|---|---|---|---|---|---|---|---|
A | A | A | A | A | -1 | 0 | 1 | 2 | 3 |
完整代码
#include<iostream>
int next[100];
void GetNext(char* p, int next[])//F数组
{
int pLen =strlen(p);
next[0] = -1;
int k = -1;//子串
int j = 0;//主串
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++k;
++j;
next[j] = k;
}
else
{
k = next[k];
}
}
}
int KmpSearch(char* s, char* p)
{
int i = 0;//主串
int j = 0;//子串
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen)
{
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
int main()
{
char str[100] = "tiemo";//子串
GetNext(str,next);
int t=KmpSearch("atiemochuandamenkoutie", str);
printf("匹配的位置是:%d\n",t);
system("pause");
return 0;
}