KMP算法是一種改進的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同時發現,所以人們稱它爲克努特——莫里斯——普拉特操做(簡稱KMP算法)。KMP算法的關鍵是利用匹配失敗後的信息,儘可能減小模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個next()函數,函數自己包含了模式串的局部匹配信息。時間複雜度O(m+n)。
————來自百度百科
簡單的說 :
1.kmp是一種字符串匹配算法。
2.它的優勢就是能經過 next[]數組(一個記憶數組),減小匹配的次數, 從而節省時間。
3.關鍵是next[] 這個記憶數組。html
由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數組, 保留了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的理解入口->
本人的講解:
當匹配失敗時 主串 i會停留不動,
k 會都等於 next[k]。
一下圖片是 AAACAAAA和它next[]數組狀況正確
接下來是過程
深刻了解kmp 請看大牛的講解->