一,KMP算法
KMP算法,全称就是克努特-莫里斯-普拉特算法,是为了避免大量的重复遍历的情况。举个例子:
图 1 遍历对比匹配图1
图2 遍历对比匹配图2
从图1和图2中可以看出来,在图1中如果依次匹配,到5的时候匹配不成功,此时要回头重新匹配,从i=2,j=1的位置开始匹配,这就是回溯法的思想,就这样依次来回地回溯对比验证,最后才给出一个结论是否匹配成功。
图 3 思路启发一
思路启发一,在图3中可以看到在上面的匹配中,当i=j=5的时候,是S[5]与T[5]不相等,即不匹配,此时要重新进行匹配,分析可知S[1]=T[1],S[2]=T[2],S[3]=T[3],S[4]=T[4],而T[1]不等于T[2],T[1]不等于T[3],T[1]不等于T[4],T[2]也不等于T[3],T[2]也不等于T[4],此时就不用将T[1]与S[1]\S[2]\S[3]\S[4]相匹配,直接让T[1]与S[5]进行匹配,即如图3下面的演示那样。这个时候提高了效率。
图4 思路启发二
这个思路启发二看上去肯定不符合启发一的情况,此时T[1]与S[2]是相等的,T[1]=T[2],这个时候可以直接认为T[1]=S[2],匹配下一个,即匹配T[2]与S[3],依次匹配下去就会出现匹配成功的结果。
图 5 思路启发三
在图5中可以看到,T[1]=T[2]=S[4]=S[5],此时可以直接从T[3]开始匹配,这样就提高了效率。
图6 思路启发四
在图6思路启发四中可以看到,根据前面的一二三的经验这个时候可以很明显从T[4]开始匹配。这样得出的一些规律就是KMP算法。问题是由模式串决定的,而不是目标串决定的。主要是为了保证 i 不再回溯。避免了效率低下的问题。
二,KMP算法的实现
1,首先get_next函数的编写
#include <stdio.h>
typedef char* String;
void get_next(String T,int *next)
{
int i = 1;
int j = 0;
next[1] = 0;
while(i < T[0])
{
if(0 == j || T[i] == T[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main()
{
char str[255] = "ababaaaba";
int next[255];
int i = 1;
str[0] = 9;
get_next(str, next);
for(i=1;i<10;i++)
{
printf("%d ",next[i]);
}
return 0;
}
结果为
获取next数组的结果图
2,KMP算法的实现
//返回子串T在主串S第pos个字符之后的位置
int Index_KMP(String S,String T,int pos)
{
int i = pos;
int j = 1;
int next[255];
get_next(T,next);
while(i<=S[0]&&J<=T[0])
{
if(S[i] == T[j])
{
i++;
j++;
if(T[i]!=T[j])
{
next[i] = j;
}
else
{
next[i] = next[j];
}
}
else
{
j = next[j];
}
}
if(j>T[0])
{
return i - T[0];
}
else
{
return 0;
}
}