KMP模式匹配算法

KMP 字符串匹配算法,用它来实现字符串的匹配,效率很高,特别是当字符串很长的时候。
prefix_table();是一个前缀计算函数。
首先前缀的计算只与模板有关,而与原字符串无关。
如:ABCABDABCAB前缀为
00012012345
第一个A前面没有与它相同的故为0,接下来为AB,A也与B不相同,ABC仍然没有一样的,ABCA,此时最后一个字符A与第一个字符A相同,故为1,
ABCAB,AB重复故为2,依次计算。其实每次计算都是从第一个字符相比较,但是再计算时,假如目前为ABCAB,我们已经知道前面的A匹配了,那我们
只需要比较新加入的字符与第二个字符是否匹配了,如果匹配,那么就加1,如果不匹配则为0.

在写程序时,模板肯定是要传入的,为了后续的使用前缀表,最好也传入一个前缀数组。

void prefix_table(char pattern[] , int prefix[] ,int patternLength)
{
	prefix[0] = 0;//前缀的第一个为0
	int patternIndex = 1;//因为第一个已经确定了
	int prefixPreviesLength = 0; //用来标记当前位置前最大的字串长度
	while ( patternIndex < patternLength )
	{
		if (pattern[patternIndex] == pattern[prefixPreviesLength])
		{
			prefixPreviesLength++;
			prefix[patternIndex] = prefixPreviesLength ;
			patternIndex ++;
		}
		else
		{
			if( prefixPreviesLength > 0)///保证不越界
					prefixPreviesLength = prefix[prefixPreviesLength-1];
					//如果不相等则调整,当前位置最大的字符串长度,变为模板的前一个前缀,继续比较
			else
			{
				  prefix[patternIndex] = 0; ///已经找不到了,为0
				  patternIndex++;//计算下一个的前缀
			}
		}
		
	}

}

一般为了后续程序的书写,将得到的前缀表右移一位,将prefix[0] = -1;
void move_prefix_table(int prefix[] ,int n)
{
	for (int i = n-1 ; i > 0 ; i -- )
	{
		prefix[i] = prefix[i - 1];
	}
	prefix[0] = -1 ;
}

  准备工作都做好,最后就是与原字符串的匹配了。在匹配前,先完成对前缀表的计算

    prefix_table(pattern, prefix ,PatternLength );
    move_prefix_table(prefix ,PatternLength );

   在匹配过程中,原字符串的索引是一直增加的,不会回溯,这也就是为什么KMP算法相比暴力搜索法高效的原因。

   当当前的模板索引与模板长度-1相同时,判断当前的字符与原字符串中的字符是否相等,如果相等,就输出它匹配的位置,

并将前字符的前缀值赋予索引,继续查找,寻找是否还有相匹配的。

void kmp_search(char data[] , char pattern[] )
{
	int DataLength = strlen(data);
	int PatternLength = strlen(pattern);
	int* prefix = new int[PatternLength];
	prefix_table(pattern, prefix ,PatternLength );
	move_prefix_table(prefix ,PatternLength );
	int DataIndex = 0;
	int PatternIndex = 0;
	while ( DataIndex < DataLength )
	{
		if (PatternIndex == PatternLength-1 && data[DataIndex] == pattern[PatternIndex])
		{
			printf("Found pattern at %d . \n" , DataIndex-PatternIndex);
			PatternIndex = prefix[PatternIndex];
		}
		if ( data[DataIndex] == pattern[PatternIndex])
		{
			DataIndex++;
			PatternIndex++;
		}
		else
		{
			PatternIndex = prefix[PatternIndex] ;//回溯查找,回溯到适当的位置
			if ( PatternIndex == -1)
			{
				PatternIndex++;
				DataIndex++;
			}

		}
	}
}

  

int main(int argc, char* argv[])

{
	
	char data[] = {'a','a','x','a','a','x','\0'};
	char pattern[] = {'a','a','x','\0'};
	kmp_search(data ,pattern );

	char *data1 = "aaasaadfgdsasvcd";
	char *pattern1 = "as";
	kmp_search(data1 ,pattern1 );

	return 0;

}

结果为:

Found pattern at 0 .
Found pattern at 3 .
Found pattern at 2 .
Found pattern at 11 .

注意:若使用的是数组,需要在传递的时候,在数组的最后加上\0,因为strlen只有检测到\0才会停止。

 

 

posted on 2019-05-07 09:20  逆光也很美  阅读(351)  评论(0编辑  收藏  举报

导航