1. 程式人生 > >kmp匹配演算法介紹及實現

kmp匹配演算法介紹及實現

KMP匹配演算法

最近在看程傑的《大話資料結構》一書,看到了第五章,這一章介紹了對串進行匹配的演算法,包括樸素模式匹配演算法和KMP模式匹配演算法。對於KMP演算法自己也是搞得有點暈乎了,在這裡記錄下,以後說不定徹底弄懂了就回來補上。

  • KMP演算法是由D.E.Knuth、J.H.Morris和V.R.Pratt三位前輩共同發表的一個模式匹配演算法,該演算法可以大大避免重複遍歷的情況。

  • 我們把要查詢的字串稱為模式串pattern,相應的在某個字串中進行查詢是否包含模式串的稱為文字串text,也稱為主串。

  • KMP演算法的核心是字首表,也稱為next陣列的計算,next數組裡面存放的是模式串的最長前後綴的相似度。所謂前後綴是指模式串中的某個子串,而最長字首是指:該子串第一個字元開始,不包含最後一個字元的串,而最長字尾是指:該子串不包含第一個字元的串。

例子

比如模式串為abcabcd,其字首分別是: a ab abc abca abcab abcabc abcabcd 這些前後綴的相同的最長前後綴分別是: 0 0 0 1---->a 2---->ab 3---->abc 0 所以next陣列的值是{0, 0, 0, 1, 2, 3, 0} 。

程式碼實現

  • 以上便是自己理解的部分,下面是實現程式碼:

    獲取字首表:   
       void get_prefix_table(const char *pPatternStr, int *next)
       {
            if(NULL == pPatternStr) return;
          	
          	int i = 1;
          	int j = 0;
          	int len = strlen(pPatternStr);
          	
          	if(len == 0) return;
          	
          	next[0] = 0; //規定是0
          	
          	while(i < len)
          	{
          		if(pPatternStr[j] == pPatternStr[i])
          		{
          			j++;
          			next[i] = j;
          			i++;
          		}
          		else
          		{
          			if(j > 0)//防止陣列越界
          			{
          				j = next[j-1]; //回溯
          			}
          			else
          			{
          				next[i] = 0;  
          				i++; //移動到下一位繼續
          			}
          		}
          	}
          	for(i=len-1; i>0; i--)//next陣列往後移動一位,便於KMP演算法匹配實現
          	{
          		next[i] = next[i-1];
          	}
          	next[0] = -1;
            }  
         
          /*
          KMP演算法實現
          字串匹配
          main_str: 主串
          sub_str: 子串
          pos: 從主串main_str的第pos位開始往後查詢與子串sub_str相匹配的串
          return: 返回子串sub_str相匹配的第一個串的起始位置, 沒有匹配則返回-1
          */
          int kmp_search(const char *main_str, const char *sub_str, int pos)
          {
          	if(pos<0 || !main_str || !sub_str) return -1;
          	
          	int i = pos;
          	int j = 0;
          	int main_len = strlen(main_str);
          	int sub_len = strlen(sub_str);
          	
          	if(pos>main_len || !sub_len || !main_len) return -1;//越界或者空字串
          	
          	int *next = (int *)malloc(sizeof(int) * sub_len);
          	
          	get_prefix_table(sub_str, next);
          	
          	while(i<main_len && j<sub_len)
          	{		
          		if(j == sub_len-1 && main_str[i] == sub_str[j])
          		{
          			free(next);
          			return i-j;
          		}
          		if(main_str[i] == sub_str[j])
          		{
          			i++;
          			j++;
          		}
          		else
          		{
          			j = next[j];
          			if(-1 == j)
          			{
          				i++;
          				j++;
          			}
          		}
          	}
          	free(next);
          	return -1;
          }