关键点:公共前后缀字符串
1.首先按照BF比较后,出现的那个不匹配的字符往前找,比如图中是AB。每次要取最长公共前后缀字符串,比如图中最长的是AB。
2.然后,把最长前缀移到最长后缀的位置,再从最长后缀开始比较。
这是按照移动模式串(子串)的思维来的。实际上操作是这样:
每次开始比较的编号,等于最大公共前后缀长度+1
如图中,到了模式串的第七个了,往前找,最大公共前后缀长度是1,这个时候把前缀移到后缀的位置,即将1移到6,那么接下来就是2号位与主串当前位比较。
前后缀长度为n,则n+1号位于主串当前位置比较
这样以来,就得到了next数组。
比如刚才next[7]=2
前后缀长度为n,则n+1号位与主串当前位置比较
前后缀长度为1,则2号位与主串当前位置比较,所以next[7]=2
next[i]表示模式串中,前面长度为i的串的公共最长前缀和后缀长度。
一旦在匹配的时候在某个位置失配之后,直接依靠next数组跳到相同后缀的前缀部分。(相当于后缀的下一个匹配失败,则直接将位置跳到前缀的部分,因为我们next数组已经预处理了,O(1)实现。),
这时候继续比较该位置和前缀的下一个字母是否匹配,不匹配继续按上面的步骤跳转,匹配则匹配下一个字母直到完全匹配。
匹配失败相当于后缀的下一个匹配失败了,我们就可以直接跳到与后缀相同的前缀,此时前面依然是完全匹配的,继续比较下一个。
/*计算当前要匹配的模式串的next数组*/
void get_next(String T,int *next){
int i,j;
i=1;
j=0;
next[1]=0
while(i<T[0]){ //T[0]表示模式串的长度
/*T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
*如果后缀字符和前缀字符相等,最后就得到一个next数组某一位的值*/
if (j==0||T[i]==T[j]){
++i;
++j;
next[i]=j;
}
else
j=next[j]; //若字符不相同,则j值回溯
}
}
/*执行KMP算法*/
int Index_KMP(String S,String T,int pos){
/*i用于主串S当前位置的下标志。若pos不为1,则从pos位置开始匹配*/
int i=pos;
/*j用于模式串T当前位置的下标值*/
int j=1;
/*定义一个next数组*/
int next[255];
/*求模式串T的next数组*/
get_next(T,next);
/*若i小于S的长度,且j小于T的长度,则持续循环*/
while(i<=S[0]&&j>=T[0]){
/*两字母相等则继续(相对BF增加了j=0判断)*/
if (j=0||S[i]==T[j]){
++i;
++j;
}
else {
/*指针后退后重新开始匹配
*即:j退回合适的位置,i值不变*/
j=next[j];
}
}
if (j>T[0])
return i-T[0];
else
return 0;
}
普通的BF暴力比较:
- 对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。
- 对主串做大循环,每个字符开头做子串长度的小循环,直到匹配成功or全部遍历完成。
实现代码:
/*返回子串T在主串S中第pos个字符后的位置。
*若不存在,则函数的返回值为0
**T非空,1<=pos<=StrLength(S) */
int Index(String S,String T,int pos){
/*i用于主串S当前位置的下标志。若pos不为1,则从pos位置开始匹配*/
int i=pos;
/*j用于模式串T当前位置的下标值*/
int j=1;
/*若i小于S的长度,且j小于T的长度,则持续循环*/
while(i<=S[0]&&j>=T[0]){
/*两字母相等则继续*/
if (S[i]==T[j]){
++i;
++j;
}
else {
/*指针后退后重新开始匹配
*即:i退回到上次匹配首位的下一位,j退回到模式串T的首位*/
j=next[j];
}
}
if (j>T[0])
return i-T[0];
else
return 0;
}