KMP算法
kmp算法是一种在一个串里寻找是否有另外一个串的强力算法,其核心是在匹配失败时寻找到上一次匹配成功的位置继续匹配,从而大大提高效率
KMP算法依靠于前缀表,前缀表记录了一串字符串以每一个位置为终点位置时的最大相等先后缀
那最大相等前后缀是什么?
首先得提及前缀和后缀
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串;
例如一串字符AABAAF
这串字符的前缀为:
- A
- AA
- AAB
- AABA
- AABAA
后缀为:
- F
- AF
- AAF
- BAAF
- ABAAF
注:前后缀的看法都是从左往右看,所以相等前后缀都是从左往右比较,而不是对称!
那我们来寻找最大相等前后缀
将一串字符串挨个寻找
- A :严格意义来说它并没有前后缀,因为在这串字符串内它既是首字符,也是尾字符,所以它的最大相等前后缀为0;
- AA:它不含尾字符的前缀有A(第一个A)不含首字符的后缀有A(第二个A)两者相等,所以它的最大相等前后缀为1;
- AAB:因为后缀含有B,所以找不到相等的前缀,为0
- AABA,前缀有A,AA,AAB;后缀有A,BA,ABA;有一个相等前后缀,为1
- AABAA,前缀有A,AA,AAB,AABA;后缀有A,AA,BAA,ABAA;有两个相等的前后缀,为2;
- AABAAF,因为后缀有F,所以找不到相等前缀,为0;
于是就有以下前缀表:
A A B A A F
0 1 0 1 2 0
这个前缀表的作用是什么?
首先得回到字符串匹配的问题上:
给你两串字符串,文本串和模板串
文本串:AABAABAAF
模板串:AABAAF
当我们一个一个字符匹配的时候,模板串前5个字符都能与文本串匹配,到第6个字符F并不能与B匹配,这时该怎么办呢?
则需要回到我们的前缀表,找到我们的最大前后缀为2,则此处的A从它开始数两个,即为AA
后缀处有一个这样的AA,前缀处也有一个这样的AA,我们在此处后缀的后方不匹配,则需要到达前缀处的后方进行重新匹配;
即从B开始重新匹配,将B即以后的字符串与原来F的位置(匹配失败的位置)开始匹配
即(将真前缀跳到真后缀处)
即
文本串:AABAABAAF
模板串: AABAAF
这样即匹配成功
那代码该如何实现?
首先需要一个数组next[],用于记录当前字符串配对失败时,应该跳转的位置;
关于数组next[]
需要有
- 数据初始化
- 前后缀不同情况
- 前后缀相同情况
- 数据更新
初始化:首先定义i,j;j指向前缀末尾位置,i指向后缀末尾位置,所以j从0开始,i从1开始;
前后缀不同时,即:s[j]!=s[i]时,将j回退,回退到的下标即为next[j]的值
相同时则模式串位置移动,继续匹配,更新next[]数组的值;
j=0;
next[0]=0;
for(i=1;i<=s.size();i++)
{
while(j&&s[j+1]!=s[i])//如果不匹配
{
j=next[j];//回退
}
if(s[j+1]==s[i])//如果匹配
{
j++;//移动
}
next[j]=j;//更新next数组
}