BF算法中:
匹配串: —-A A A A B C C —
模式串:—–A A A A A B C —
后移后:——- A A A A A B C
在位置4匹配失败,按照BF算法,下一轮匹配开始,匹配串指针只会向后后偏移一位,
KMP 算法是对BF算法的改进,通过预处理,来改进每次向后的偏移量.
KMP算法实在有点绕。看了很多文章都是描述不清,但这篇文章简单易懂,强烈推荐。
内容不在赘述。对前后缀不明白的还可以看这篇文章。
对前后缀还有一种情况需要说明,那就是出现多个匹配项是取最长的匹配项:
比如: P = { AAAAB }
P[0] = { A } ,前缀为{ ∅} ,后缀为{ ∅ } 只有空集匹配,那么匹配值为length{∅}=0
P[1] = { AA },前缀为{ ∅ , A } 后缀为{ A,∅ } 出现了1个非空匹配项,匹配值为length{A} = 1
P[2] = { AAA } ,前缀为{ ∅ ,A ,AA } ,后缀为{AA ,A,∅ }出现了两个非空的匹配的{A},{AA}匹配值取max(length{A},length{AA})=2
P[3] = { AAAA } ,前缀为{∅,A,AA,AAA},后缀为{AAA,AA,A,∅},出现了3个非空的匹配项,匹配值取max(length{A},length{AA},lenght{AAA}) =3
P[4] = { AAAAB },前缀为{∅,A,AA,AAA,AAAA} 后缀为{AAAB,AAB,AB,B,∅} 只有空集匹配,匹配值为length{∅} = 0;
所以得到部分匹配表(partial match table ) pmt={0,1,2,3,0}
KMP算法 其实只改进了BF算法中一句(s++),增加后移偏移量。
KMP实现代码如下:
int* KMP_build_pmt( const char* P ,int plen )
{
int *pmt = new int[ plen ];
memset( pmt , 0 , plen * sizeof(int) ); //全部初始为0
const char* current_begin;
const char* current_end; //[current_begin,current_end] 当前要匹配的字符段
const char* current_prefix_begin;
const char* current_suffix_begin;//[current_prefix_begin,current_prefix_begin+x]比较的前缀子缀,[current_suffix_begin,current_suffix_begin+x]比较的后缀的子缀
for ( int i = 0; i < plen ; ++i ) //对于P的不同长度的子缀 P[n] = {AAA}
{
current_begin = P;
current_end = P + i;
for ( int j = 0 ; j < i ; ++j ) //对于P[n],不同长度的子缀,分别判定当前各个子缀是否相等
{
int k = j + 1; //子缀的长度
current_prefix_begin = current_begin;
current_suffix_begin = current_end - j;
//这里可以自己写个遍历来判定两个是否相等,简洁起见使用strncmp
if ( strncmp( current_prefix_begin , current_suffix_begin , k ) == 0 ) //子缀匹配成功
{
pmt[i] = k > pmt[i] ? k : pmt[i]; //取较大者
}
}
}
return pmt;
}
const char* KMP( const char* S , const char* P )
{
if ( !S || !P )
{
return NULL;
}
if ( !*P )
{
return S;
}
int plen = strlen( P );
int* pmt = KMP_build_pmt( P , plen );
char *s = (char*)S;
char *s1,*s2;
const char* match = NULL;
while( *s )
{
s1 = s;
s2 = (char*)P;
while( *s1 && *s2 && !(*s1-*s2) )
s1++,s2++;
if ( !*s2 )
{
match = s;
break;
}
//s++;
//kmp 算法相对于BF算法,其实就只改了这一句!
//s2 == P 那么 这次第一个字符就失败了,直接偏移1,同s++
//如果不等,s2 - P 表示匹配成功了多少个字节。那么本次偏移为 bias = 以匹配数量 - 对应部分的匹配值
//匹配串: A A A B C
//模式串: A A A C
//s2在C处失配,s2指向模式串的中C s2 - P = 3表示已匹配3个字节, s2-P-1 表示最后一个匹配字符的在模式串中的index(这里是最后一个A的index)
s += ((s2 == P) ? 1 : (s2 - P - pmt[ s2 - P - 1 ] ));
}
delete pmt;
return match;
}
时间复杂度O(strlen(S)+strlen(P)).
空间复杂度O(strlen(P))
KMP 算法和BF算法都属于前缀匹配算法。在实际使用中KMP效率并不会比BF效率高出多少。