KMP算法----代碼實現篇

通過學習kmp的思想和實現步驟,我們已經初步理解了kmp算法的運行過程,那麼接下來必然要學會代碼實現。現在,進行代碼實現吧!

kmp主要就是兩步:

第一步,求出前綴表,也就是常說的next數組;
第二步,進行具體的匹配過程。

首先,我們一起來用代碼解決kmp算法中的前綴表吧!

我們再次回顧前綴表的構建方法(請移步到我們上一篇博客: KMP算法—講解篇)。
這次我們會和上次講的前綴表建立有一點點不同(但其實就是換湯不換藥,我會慢慢說明的):
在這裏插入圖片描述
在這裏插入圖片描述
我們可以對比一下臨近的兩行,比如:
在這裏插入圖片描述
在此,假如我們已經獲得了上圖第一行的前綴表數據(當前是1),那麼我們只需判斷下一位B(p[6])是否和B(p[1])相同,如果相同的話,那自然而然這一行對應的前綴表數據就是1++了;如果不相同呢?
我們暫且不討論不同的情況(狗頭保命),我們先把現有的思路實現了。

//pattern是我們現有的模式串,prefix是我們要求的初代前綴表(因爲沒有移動),n是串的長度
void prefix_table(char pattern[],int prefix[],int n){
	prefix[0]=0;//不解釋(只有一個元素的串嘛,翻一翻上面的那個每行對應的數字奧)
	int len =0;//len,是用來比較的長度,具體作用當你模擬幾次就會了解。
	int i=1;//用來標是當前所求的位置下標
	while(i<n){//i<n的時候循環
		if(pattern[i]==pattern[len]){
                    //len就是上一次求完之後的公共串長度,判斷pattern[len]和現在位置是否相同
			len++;//長度加1
			prefix[i]=len;//當前的前綴表數據就是加1之後的len
			i++;//往後移動一位
		}
		else {
			//這裏就是我們發現兩個不相同時的情況。
		}
	}
}

下來,我們討論不相同時的情況。
在這裏插入圖片描述
看到這,可能就有疑問了:不是說這些都要往後移動一格嗎?第一個不是添加-1嗎?

這就是我在開頭所說的那一點點不同(其實都是一樣的),在這裏用代碼實現前綴表的過程中,我們先像這樣對齊的建立,再都往後移動一格,就好了。

好了我們繼續回到問題。
我們發現此時的len=3,i=8(假如我們已經進行到當前位置了)。明顯pattern[3]和pattern[8]不相同,那麼我們把len更新爲prefix[len-1];len=1再次對比,發現pattern[len]和pattern[8]不相同,我們繼續把len更新爲prefix[len-1],len=0;發現pattern[len]和pattern[8]相同,所以len++,prefix[8]=1;
完善代碼(包含移位的過程):

//pattern是現有的模式串,prefix是要求的初代前綴表(因爲沒有移動),n是串的長度
void prefix_table(char pattern[],int prefix[],int n){
	prefix[0]=0;//不解釋(只有一個元素的串嘛,翻一翻上面的那個每行對應的數字奧)
	int len =0;//len,是用來比較的長度,具體作用模擬幾次就會理解。
	int i=1;//用來標是當前所求的位置下標
	while(i<n){//i<n的時候循環
		if(pattern[i]==pattern[len]){
                    //len就是上一次求完之後的公共串長度,判斷pattern[len]和現在位置是否相同
			len++;//長度加1
			prefix[i]=len;//當前的前綴表數據就是加1之後的len
			i++;//往後移動一位
		}
		else {
			//這裏就是我們發現兩個不相同時的情況。
                   //爲了防止出現我們求prefix[1]的時候訪問到pattern[-1]
                   if(len>0){
				len = prefix[len-1];
                            //正常情況錯位更新len(因爲是到了len-1嘛,所以稱爲len-1)
			}
			else {
				prefix[i]=len;//這裏len就是0,所以寫成0也是可以的
				i++;
			}
		}
	}
	//現在實現移位 
	for(i=n-1;i>0;i--){
		prefix[i]=prefix[i-1];
	}
	prefix[0]=-1;
}

到這裏,已經簡述了前綴表的建立,下面就是完整kmp算法的實現:

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
//pattern是現有的模式串,prefix是要求的初代前綴表(因爲沒有移動),n是串的長度
void prefix_table(char pattern[],int prefix[],int n){
	prefix[0]=0;//不解釋(只有一個元素的串嘛,翻一翻上面的那個每行對應的數字奧)
	int len =0;//len,是用來比較的長度,具體作用模擬幾次就會理解。
	int i=1;//用來標是當前所求的位置下標
	while(i<n){//i<n的時候循環
		if(pattern[i]==pattern[len]){
                    //len就是上一次求完之後的公共串長度,判斷pattern[len]和現在位置是否相同
			len++;//長度加1
			prefix[i]=len;//當前的前綴表數據就是加1之後的len
			i++;//往後移動一位
		}
		else {
			//這裏就是我們發現兩個不相同時的情況。
                   //爲了防止出現我們求prefix[1]的時候訪問到pattern[-1]
                   if(len>0){
				len = prefix[len-1];//正常情況錯位更新len(因爲是到了len-1嘛,所以稱爲len-1)
			}
			else {
				prefix[i]=len;//這裏len就是0,所以寫成0也是可以的
				i++;
			}
		}
	}
	//現在實現移位 
	for(i=n-1;i>0;i--){
		prefix[i]=prefix[i-1];
	}
	prefix[0]=-1;
}
void kmp_search(char text[],char pattern[]){//主串text,模式串pattern 
	int n=strlen(pattern);
	int m=strlen(text);
	int *prefix=(int *)malloc(sizeof(int)*n);//建立一個前綴表數組,也即是next數組 
	prefix_table(pattern,prefix,n);//調用建立前綴表數組函數 
	int i=0,j=0,flag=0;
	//text[i]         ,len=m    用i和m表示主串的下標和長度 
	//pattern[j]      ,len=n    用j和n表示模式串的下標和長度 
	while(i<m){
		if(j==n-1&&text[i]==pattern[j]) {
			printf("We find it in %d\n",i-j);//輸出模式串在主串中的位置 
			flag=1; 
			j=prefix[j];//匹配成功 ,我們可以直接退出,當然有些主串和模式串並不只在一個地方包含 
		}
		if(text[i]==pattern[j]){//正常匹配,往後+就行 
			i++;
			j++;
		}
		else {//匹配失敗,失配位和前綴表中數據所指的項對齊 
			j=prefix[j];
			if(j==-1){
			//這就是我們訪問到了那個開頭的-1,這個時候,我們當然是讓模式串往後移動一格,並且和主串的下一個元素匹配 
				i++;
				j++;
			}
		}
	}
	if(flag==0){
		printf("We can't find it in text\n");//flag=0,未找到 
	} 
}
int main()
{
	char pattern[]="ABABCABAA";
	char text[]="ABABABCABAABABABABCABAA"; 
	//char text[]="ABABABCAAABABABABCAAA"; 
	kmp_search(text,pattern);
	return 0;
 } 

以上就是本人對KMP淺顯的理解,有不足之處希望大佬指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章