1. 程式人生 > >KMP演算法——快速求失效函式值及其程式碼實現

KMP演算法——快速求失效函式值及其程式碼實現

  • 字首和字尾的最大相等長度

為了更好的理解我接下來所說的回溯值的求法,這裡先介紹一下,如何求一個字串的字首和字尾相等的最大長度,為了便於說明記為k。

注意:字首和字尾不能為字串本身!!!!!!

如字串“abcab”的k為2、字串“a”的k為0、“aaaaa”的k為4、“abcaabc”的k為3。

  • 回溯值

        在主串T中尋找模式串P的過程稱為模式匹配。若匹配成功,則返回P在T中的位置,匹配失敗,返回0;常用的演算法有兩種,一個是樸素的模式匹配演算法,這裡就不做過多介紹。另一個就是看書看到頭暈的KMP演算法~~

     這裡首先來大概講一講KMP演算法思想的關鍵在於那裡,先通過圖來看一下,KMP演算法匹配的過程

上圖:i=0,j=0;i=1,j=1;i=2,j=2時字元都是匹配的,當i取3,j取3的時候,字元不匹配,這時候,如果按樸素的模式匹配演算法來求,這時候需要將i退回i=1處,j退回j=0處。但我們觀察發現,j之前的字串和主串中一部分已經完全匹配了,而“aba”的k(上一個標籤中出現)為1,這時i不動,讓j移到k處,即j=1;“aba”的字首a已經和字尾a相等,而後綴a已經和主串中的a匹配了,所以這個時候已經不需要再匹配a。如下圖:

以這樣一個小小的例子我們可以發現,關鍵就是求得當p[j]與T[i]不匹配時,j應該退回到模式串的什麼位置。模式串中每一個位置都有它對應的回溯值。這樣一個和模式串下標有關的陣列記為next[]。

手算快速求出next【】呢???

如“abaabcac”,預設當j=0時,next[0]=-1。

當j=1是,拿出他之前的子串,即“a”,k值為0,所以next[1]=0;

當j=2時,拿出他之前的子串,即“ab”,k為0,所以next[2]=0;

當j=3時,拿出他之前的子串,即“aba”,k為1,所以next[3]=1;

當j=4時,拿出他之前的子串,即“abaa”,k為1,所以next[4]=1;

當j=5時,拿出他之前子串,即“abaab”,k為2,所以next[5]=2;

當j=6時,拿出他之前子串,即“abaabc”,k為0,所以next[6]=0;

當j=7時,拿出他之前的子串,即“abaabca”,k為1,所以next[7]=1;

所以模式串P的失效值的陣列為[-1,0,0,1,1,2,0,1];

  • 失效函式值的兩種實現

  • 求模式失效函式值的遞迴演算法

int getNext(int j,char p[]){
	if(j==0){
		return -1;
	}
	if(j>0){
		int k=getNext(j-1,p);
		while(k>=0){
			if(p[k]==p[j-1]){
				return k+1;
			}else{
				k=getNext(k,p);
			}
		}
		return 0;
	}
	return 0;
} 
  • 求模式失效函式值的遞推演算法

    void getNext(char p[],int next[]){
    	int j=0;
    	int k=-1;
    	next[0]=-1;
    	while(p[j]){
    		if(k==-1||p[k]==p[j]){
    			j++;
    			k++;
    			next[j]=k;
    		}else{
    			k=next[k];
    		}
    	}
    }
  • 完整的KMP演算法

    #include <iostream>
    #include <cstring> 
    using namespace std;
    void getNext(char p[],int next[]){
    	int j=0;
    	int k=-1;
    	next[0]=-1;
    	while(p[j]){
    		if(k==-1||p[k]==p[j]){
    			j++;
    			k++;
    			next[j]=k;
    		}else{
    			k=next[k];
    		}
    	}
    }
    int kmpCheck(char T[],char P[]){
    	int n=strlen(P);
    	int next[n]; 
    	getNext(P,next);
    	int i=0,j=0;
    	while(T[i]&&P[j]){
    		if(j==-1||T[i]==P[j]){
    			i++;
    			j++;
    		}else{
    			j=next[j];
    		}
    	} 
    	if(P[j]=='\0'){
    		return (i-j);
    	}else{
    		return -1;
    	}
    }
    int main(){
    	char T[]="absabaabcac";
    	char p[]="abaabcac";
    	int index=kmpCheck(T,p);
    	cout<<index<<endl; 
    }

              本來想詳細講解一下KMP演算法的原理,後來發現我自己的語言表達確實有點拙劣,倒是花了很長的時間畫圖,也簡單說了一下如何手算一個kmp演算法的回溯值矩陣。至於演算法的實現就是草草的貼了幾個程式碼。能力有限,還希望不當之處大家及時和我講出來。