KMP算法的學習經驗

KMP算法的學習經驗

(歡迎指正錯誤, 歡迎噴)

  1. 什麼是kmp(完)
  2. kmp的額外知識(完)
  3. 暴力匹配的缺點,和代碼實現(完)
  4. next[]數組的預先知識,瞭解先後綴,相同先後綴。(完)
  5. kmp的關鍵next[]數組, next的實現。(完)
  6. next數組中k = next[k]的理解。
  7. kmp的優化(待續。。)

一.什麼是kmp

KMP算法是一種改進的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同時發現,所以人們稱它爲克努特——莫里斯——普拉特操做(簡稱KMP算法)。KMP算法的關鍵是利用匹配失敗後的信息,儘可能減小模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個next()函數,函數自己包含了模式串的局部匹配信息。時間複雜度O(m+n)
————來自百度百科
簡單的說 :
1.kmp是一種字符串匹配算法。
2.它的優勢就是能經過 next[]數組(一個記憶數組),減小匹配的次數, 從而節省時間。
3.關鍵是next[] 這個記憶數組。html

二.kmp的額外知識

由D.E.Knuth,J.H.Morris和V.R.Pratt同時發現,所以人們稱它爲克努特——莫里斯——普拉特操做(簡稱KMP算法)
而且kmp被不少人叫作,看(k)毛(m)片(p)算法。web

三.普通暴力匹配


//ptr[] 匹配串, str[] 主串算法

1.暴力匹配
咱們的普通暴力匹配是 匹配串ptr 的字符一個一個和 主串匹配。
若是一個字符匹配成功:ptr的下一個字符繼續匹配,
若是失敗 :str[]回溯到開始匹配的位置(i = i - j, <-下面有講解),而後再以 下一個位置爲開始, 再次從新進行字符串匹配數組

暴力匹配代碼:svg

int plen = strlen(ptr);//匹配串的長度
int slen = strlen(str);//主串的長度
int i, j = 0; //i 和j 分別表明 當前 str 和ptr 所匹配的字符下標
int flag = 0;//若是爲 1 表示 主串中存在 匹配串。
for(i = 0;i < slen;i++)
{
	if(str[i] == ptr[j])//若是匹配成果 繼續下一個匹配
	{
		j++;
	}
	else//若是 不相等 
	{
		i = i- j;//主串回溯, i回到匹配前的位置
		j = 0;//匹配串 要從頭開始
	}
	if(j == plen)// 主串中存在客串 匹配串成功
	{
		flag = 1;
		break;//結束
	}
}

以上是暴力匹配 的代碼, 這也能解決字符串匹配的問題(這種方法沒有一點錯誤), 然而你用這種方法去解題,不少題只能送你個:
在這裏插入圖片描述
acmer的你,看到這就是很尷尬了。在這裏插入圖片描述
爲何呢?
由於你暴力匹配太盲目了, 而kmp確實聰明的 kmp的j知道往哪跳。函數

四. 相同先後綴的講解:

1.什麼是先後綴。
如:ABCD這個字符串
A
AB
ABC
就是他的前綴。學習

BCD
_CD
__ D
就是它的後綴。
·下劃線只是營造效果, 沒有意義。優化

2.相同先後綴:
如:ABCAB這個字符串
AB(前綴) AB(後綴) 就是類似先後綴(並且AB是最長的類似先後綴)。spa

五.kmp的next數組

kmp 經過next數組, 保留了ptr(匹配串)以每一個字符結尾的子串的 最長相同先後綴的長度。經過next 主串就不用回溯, 只需ptr(匹配串)來回的跳(有想法的跳 o(* ̄︶ ̄ *)o)就行。
{
如:1. 主串ABCABCABD 客串ABCABD
2.當匹配到:
ABCABCABD
ABCABD 與其失配。
可是咱們已經匹配了ABCABCABD 這個AB 咱們已經知道他匹配,
因此根據ptr的 先後綴類似的性質(ABCABD), 直接跳到類似前綴的哪裏 接着日後匹配。
因此就要知道next數組怎麼得到。.net

}

那麼接下來就看個有圖的栗子(例子):
主串:ABABAC
匹配串: ABAC
A B A B A C
A B A C

在這裏插入圖片描述

/***next數組的獲取*/
int Get_next()
{
    int plen = strlen(ptr);
    next[0] = -1;
    int k = -1;//表示上一次匹配的最長先後綴長度
    for(i = 0; i < plen; i++)
    {
        if(k == -1 || ptr[i] == ptr[k])// ptr[i]是後綴的對後一個字符, ptr[k]是前綴的最後一個字符。 k = -1是以第一個字符結尾的子字符串沒有先後綴,所以就要有個操做, 解決 第一個字符串就匹配失敗的狀況。
        {
            k++;//若是本次匹配成功 , 長度加一
            next[i+1] = k;//數組儲存
        }
        else//不成功
        {
            i--;//主串不動
            k  = next[k];//長度縮減再次匹配, 對於k爲何等於next[k] ,後續會有講解。
        }
    }
}
/***kmp 匹配過程****/
int kmp(char str[], char ptr[]{
    int flag = 0; // flag = 1 表示 主串中含有 匹配串
	int slen = strlen(str);
	int plen = strlen(ptr);
	int i, j = 0;
	for(i = 0;i < slen;i++)
	{
		if(j == -1||str[i] == ptr[j])
		//j == -1是:當第一個字符串就不匹配時, 整個匹配串要總體往右移一個。
		{
			j++;
		}
		else 
		{
			i--;
			j = next[j];
 //匹配失敗 j 跳到一上一個字母結尾的前綴後面+1處,
		}
		if(j == plen)
		{
		  flag = 1;
		  break;
		}
	}
	return flag;
}

六. k = next[k]的理解:

其餘大佬對k = next的理解入口->
本人的講解:
當匹配失敗時 主串 i會停留不動,
k 會都等於 next[k]。

一下圖片是 AAACAAAA和它next[]數組狀況正確

接下來是過程
在這裏插入圖片描述

深刻了解kmp 請看大牛的講解->

相關文章
相關標籤/搜索