字符串匹配算法之"The Knuth-Morris-Pratt algorithm"

    最近工作稍闲,有空看了下算法导论字符串匹配部分,自己简单归纳总结下:

介绍The Knuth-Morris-Pratt algorithm 算法前必须要介绍 模式字串前缀函数(Theprefix function for a pattern)

    首先假设 input text 为待匹配的字符串,pattern 为要匹配的字符子串;

1、The prefix function for a pattern:模式字串前缀函数π是 pattern 和自己的位移的匹配信息;

    假设当前匹配如下图示(a):

        字符串匹配算法之"The Knuth-Morris-Pratt algorithm"

    我们可以通过前面已经匹配的部分字符串信息来计算下一个需要匹配的位置(避免掉无效的匹配);

    这时候可以先计算pattern 和 已经匹配部分(对应前面pattern 自己的位移)的匹配信息,就是我们下一个需要匹配的位置;通俗来讲,就是求pattern 最长的前缀同时是已经匹配部分字串的后缀,这样这部分就可以跳过匹配了;计算如下图示(c)

    字符串匹配算法之"The Knuth-Morris-Pratt algorithm"

    可以看出字串"aba" 即是部分匹配字串(“ababa”) 的后缀,同时是pattern("ababaca")的前缀,其长度为3,即可以跳过pattern 前3位,直接用图示(a)不匹配的 位‘a’ 和 pattern[3] 来匹配:

    

2、下面我们来看下前缀函数的code,以及完整KMP match algorithm:

/*
这里需要注意的是pi_array的下标是以1开始到pattern_len,
也可以自己修改成以0为开始下标,需要对算法比较
理解才行~
*/
int compute_prefix_function(char const* pattern_str, int pattern_len, int * pi_array)
{
	if(NULL == pattern_str)
	{
		printf("pattern_str is null !\n");
		return -1;
	}

	int k = 0;
	int q = 0;
	int m = pattern_len;
	pi_array[1] = 0;

	for(q = 2; q <= m; q++)
	{
		while((k>0)&&(pattern_str[k] != pattern_str[q-1]))
		{
			/*
			取pattern[0~k-1]的最大前缀同时也是后缀
			pattern[0~q-1]是已经匹配的
			*/
			k = pi_array[k];
		}

		if(pattern_str[k] == pattern_str[q-1])
		{
			k++;
		}

		pi_array[q] = k;
	}

	return 0;
}


int kmp_string_match(char const* input_str, int input_len, char const* pattern_str, int pattern_len)
{
	if(NULL == input_str || NULL == pattern_str)
	{
		printf("input_str or pattern_str is null !\n");
		return -1;
	}

	int* pi_array = new int[pattern_len+1];
	int n = input_len;
	int m = pattern_len;
	int i = 0, q = 0;

	compute_prefix_function(pattern_str, pattern_len, pi_array);

	//--test code
	for(i = 1; i< m+1; i++)
		printf("pi_array[%d] = [%d]\n", i, pi_array[i]);
	//--end of test code

	for(i = 0; i <n; i++)
	{
		while((q>0)&&(pattern_str[q] != input_str[i]))
		{
			/*
			取pattern[0~q-1]的最大前缀同时也是后缀,因为
			pattern[0~q-1]是已经匹配的,下面是整个匹配过程:
			abababacababaca
                        ababaca
                          ababaca
                                ababaca
			*/
			q = pi_array[q];
		}

		if(pattern_str[q] == input_str[i])
		{
			q++;
		}

		if(q == m)
		{
			printf("Pattern string occurs with shifts %d\n", i-(q-1));
			q = pi_array[q];
		}
	}

	delete [] pi_array;
	return 0;
}

int main(void)
{
	char input[] = "abababacababaca";
	char pattern[] = "ababaca";

	kmp_string_match(input, strlen(input), pattern, strlen(pattern));

	return 0;
}


可以看出两者两个匹配函数比较类似,因为一个是pattern 和自己的位移匹配,另一个是input text 和pattern 的匹配;具体大家可以去看下算法导论。


参考:

1、《Introduction to algorithms》




你可能感兴趣的:(Algorithm,kmp算法,字符串匹配;前缀)