KMP算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。此算法可以在O(n+m)的时间数量级上完成串的模式匹配操作。
首先看下面在一般求子串在主串中的位置的算法。
模式匹配
假设有两个串S和T:
S = ‘s1s2…sn’
T = ‘t1t2…tm’
其中0<m≤n
串的模式匹配就是子串的定位操作,即要在S中找出一个与T相同的子串。其中T称为模式串,S为主串。
一般基本思想:从主串S第pos个字符起和模式串的第1个字符比较,若相等,则继续依次比较后续字符;否则从主串S的下一个字符起再重新和模式串的第1个字符相比较。以此类推,直至模式串T中的每个字符依次和主串S中的一个连续的字符序列相等,那么就匹配成功,否则就没有匹配成功。
代码:
int index(char *S, char *T, int pos)
{
i=pos, j=0;
while( i<length(S) && j< length(T) )
{
if( *( S +i) == *(T+j) )
{ j++; i++; }
else
{ i = i - j + 1; j = 0; }
}
if(j >= length(T))
return i-j;
else
return -1;
}
此算法的匹配过程易于理解,效率也较高。不过,在有些情况下,该算法的效率却很低。例如,当模式串为‘000001’,而主串为‘00000000000000000000000001’时。
KMP算法
KMP算法其实是上面模式匹配算法的一种改进。可以发现,上面的算法每一趟匹配过程中出现字符不等时,回溯指针,如果将其改进,指针不回溯,利用已经得到的部分匹配的结果将模式串向右移动的更远一些,然后继续比较,那么算法性能会得到大大的提高。
假设有两个串S和T:
S = ‘s1s2…sn’
T = ‘t1t2…tm’
其中0<m≤n
假设匹配过程中产生“失配”(即si ≠ tj)时,主串S的第i个字符应与模式串的第k(k<j)个字符继续比较,则模式串的前k-1个字符必满足下列关系式①,且不存在k’>k满足①:
“t1t2…tk-1” == “si-k+1si-k+2…si-1” ①
而已经得到的“部分匹配”的结果是:
“tj-k+1t j-k+2…t j-1” == “si-k+1si-k+2…si-1” ②
由①和②推得下列等式:
“t1t2…tk-1” == “tj-k+1t j-k+2…t j-1”③
反之,如果模式串中存在满足③的两个子串,则匹配过程中,当主串的第i个字符与模式串的第j个字符“失配”时,仅需将模式串向右滑动至模式串中第k个字符和主串中的第i字符对齐,匹配仅需从主串的第i个字符与模式串中的第k个字符比较起继续进行。
代码:
//求出子串next数组
void get_next(char *pstr,int *next)
{
int i=0;
*next=-1;
int j=-1;
while( *(pstr+i) != '/0' )
{
if( j==-1 || *(pstr+i)==*(pstr+j) )
{
i++;
j++;
if( *(pstr+i) != *(pstr+j) )
*(next+i)=j;
else
*(next+i)=*(next+j);
}
else
j=*(next+j);
}
}
int index_KMP(char *S, char *T, int pos)
{
i=pos;
j=0;
int *next;
next=new int[length(T)];
get_next(T, next);
while( i<length(S) && j<length(T) )
{
if(*(S+i)==*(T+j) || j==0)
{ j++; i++; }
else
j=next[j];
}
if( j >= length(T) )
return i-j;
else
return -1;
}