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

    最近工做稍閒,有空看了下算法導論字符串匹配部分,本身簡單概括總結下: 算法

介紹The Knuth-Morris-Pratt algorithm 算法前必需要介紹 模式字串前綴函數(Theprefix function for a pattern) 函數

    首先假設 input text 爲待匹配的字符串,pattern 爲要匹配的字符子串; spa

一、The prefix function for a pattern:模式字串前綴函數π是 pattern 和本身的位移的匹配信息; code

    假設當前匹配以下圖示(a): 字符串

        

    咱們能夠經過前面已經匹配的部分字符串信息來計算下一個須要匹配的位置(避免掉無效的匹配); input

    這時候能夠先計算pattern 和 已經匹配部分(對應前面pattern 本身的位移)的匹配信息,就是咱們下一個須要匹配的位置;通俗來說,就是求pattern 最長的前綴同時是已經匹配部分字串的後綴,這樣這部分就能夠跳過匹配了;計算以下圖示(c)string

    

    能夠看出字串"aba" 便是部分匹配字串(「ababa」) 的後綴,同時是pattern("ababaca")的前綴,其長度爲3,便可以跳過pattern 前3位,直接用圖示(a)不匹配的 位‘a’ 和 pattern[3] 來匹配: it

    

二、下面咱們來看下前綴函數的code,以及完整KMP match algorithm: io

/*
這裏須要注意的是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 的匹配;具體你們能夠去看下算法導論。 function


參考:

一、《Introduction to algorithms》

相關文章
相關標籤/搜索