KMP算法需要提前求匹配模式串的前缀函数。这个前缀函数的定义是:一个字符串的真前缀,并且这个真前缀是该字符串的后缀,并且要求这个真前缀是符合这种条件中最长的真前缀。
原文对前缀函数的描述是:That is, Pi[q] is the length of the longest prefix of P that is a proper suffix of Pq.
在应用字符串匹配过程中,这个前缀函数起到的作用是,当匹配过程中发现不一致时,整个字符串不需要仅仅往后移一个字符来比较,而是可以往后移动若干个字符,因为根据前缀函数,我们可以得知已经匹配的模板子串中的前缀和后缀相同的部分,所以,可以直接把已匹配的子串的前缀放到子串的后缀部分即可,再进行后边的比较。
根据算法导论中的伪代码改编成如下代码:
int* computePrefixFunction(const string &P)
{
int m = P.length();
int *pi = new int[m];
pi[0] = -1;
int k = -1; //k表示第0个之前的数
for(int q = 1; q < m; ++q)
{
while( k > -1 && P[k+1] != P[q])
k = pi[k];
if(P[k+1]==P[q])
k = k + 1;
pi[q] = k;
}
return pi;
}
void kmpMatching(const string &T, const string &P)
{
int n = T.length();
int m = P.length();
int *pi = computePrefixFunction(P);
int q = -1; //number of characters matched - 1
for(int i = 0; i < n; ++i) //scan the text
{
while(q >= 0 && P[q+1] != T[i])
q = pi[q]; //next character does not match
if(P[q+1] == T[i])
q = q + 1; //next character matches
if(q == m-1){ //is all of P matched?
cout << "Pattern occurs with shift" << i - m <<endl;
q = pi[q]; //look for the next match
}
}
delete [] pi;
}
算法导论的伪代码中的字符串都是从1开始,我修改为从0开始,可能对代码的可读性造成一些影响。