1.KMP算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。
1.1 求出next数组
首先我们生成match字符串的next数组,next[i]的含义代表的是match[0…i]中,必须以match[i]结尾的后缀子串(不能包括match[0])与必须以match[0]开头的前缀子串(不包括match[i-1])最大的匹配长度。
>
void getNext(const char *p,int next[])
{
if(p==NULL)
{
return;
}
next[0]=-1;
next[1]=0;
int pos=2;
int cn=0;
while(pos<strlen(p))
{
if(p[pos-1]==p[cn])
{
next[pos++]=++cn;
}
else if(cn>0)
{
cn=next[cn];
}
else
next[pos++]=0;
}
}
但是,我们仔细考虑这么一种情况,abababc,此时的next数组为 -1 0 0 1 2 3 4。当i为3的时候,也就是说next[3]=1,此时我们要回退到第一个b那里去,然而此时我在i上的值就是b,换句话说要匹配的不是b,再回到第一个b那里去比较也没有意义,所以我们需要加一个判断,这样会让我们的next数组跳跃的更快。原先的情况只是考虑i为结尾但不包括i,这里我们把i也考虑了进去。因此,我们将之前的if判断再加上一个判断即可。
if(p[pos-1]==p[cn] && p[pos]==p[cn+1]) //如果发生了上面的情况,直接看要跳过去的next就行了。
{
next[pos++]=next[++cn];
}
else if(p[pos-1]==p[cn])
{
next[pos++]=++cn;
}
1.2 利用next数组
这里假设我们已经求出了next数组,我们从str[i]出发,匹配到了j位置的字符发现与match的字符不一致了,也就是说,str[i…..j-1]和,match[0……j-i-1]是同样的,但是str[j]!=match[j-i]。这个时候就需要回退。
此时我们已经有next数组了,next[j-i]的值 表示的是 match[0……j-i-1]的最大匹配。所以现在回退不需要回退到开头,只需要让str[j]与match[k]比较.
void kmp(const char T[],const char P[],int next[])
{
int n,m;
int i,q;
n = strlen(T);
m = strlen(P);
for (i = 0,q = 0; i < n; ++i)
{
while(q > 0 && P[q] != T[i])
{
q = next[q-1];
}
if (P[q] == T[i])
{
q++;
}
if (q == m)
{
printf("Pattern occurs with shift:%d\n",(i-m+1));
}
}
}
经过测试,比较次数是小了那么一点点~
BM算法
在当前用于查找子字符串的算法中,BM(Boyer-Moore)算法是当前有效且应用比较广的一中算法,各种文本编辑器的“查找”功能(Ctrl+F),大多采用Boyer-Moore算法。比我们在学习的KMP算法快3~5倍。