查找字符串之 KMP算法

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效率高出多少。

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/pengyou41/article/details/38390281
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞