談談對KMP演算法的一些理解

2021-10-25 07:30:59 字數 3137 閱讀 9969

這裡是我看的原文,大佬講的很清楚

下面是我自己對kmp的一些感悟和理解

首先,定義一些概念:

1.在某個字串中找自己想要的字串,那麼【自己想要的字串】就被稱為模式串;

2.字串的字首是指從左到右看,始於第乙個字元,字尾也是從左到右看,終於最後乙個字元,前字尾相等就相當於字首可以通過平移的方式到達字尾的位置,與字尾相重合,相重合(注意這個和回文的區別);

說到字串的匹配問題,

首先,不得不cue一下bf演算法,所謂的暴力匹配法,但是由於其效率過於低下(具體表現為每次發現某一位不匹配的時候,都是將模式串往後移動【一位】)

擴寫著地說,bf演算法是一種"不論在哪個位置發生不匹配,都是把原字串的指標右移一位,並把模式串的指標移到首位",然而"不論在哪個位置發生不匹配"就是其效率低的根本原因,因為它沒有吸取之前某一位匹配成功和失敗的教訓,於是大佬們就從這一點出發,利用匹配成功或失敗的教訓,對演算法進行了優化

**於是就對問題有了這樣的**:**通過對之前匹配成功和失敗資訊的運用,失敗後原字串是不是可以移動多位呢?模式串的指標能不能不回到首位呢?

事實上,在kmp演算法中,原字串還是一位一位地移動,但是模式串的指標卻可以從原來的每次都「從頭開始」變成"回溯到之前的記憶位"

下面,我們來看一看kmp演算法:(一共分為兩步)

next_table有什麼用?

前面說到,匹配失敗後的模式串並不會回到首位,而是回到之前的記憶位,而next_table就相當於是乙個用於記憶的陣列,其與最長公共前字尾的大小有關,為前p - 1個字串的最長公共前字尾 - 1;

next_table怎麼形成?

這裡本質也是乙個模式串自己對自己的kmp運算

下面貼上大佬部落格上的**

void

cal_next

(char

*str,

int*next,

int len)

if(str[k +1]

== str[q]

)//如果相同,k++

next[q]

= k;

//這個是把算的k的值(就是相同的最大字首和最大字尾長)賦給next[q]

}}

我起初直接取看**,結果是一臉懵逼的,下面我按照原文裡的思路,新增一些細節:

第一步是給next[0]賦值,然後進入for迴圈

先看整體框架,發現每一次for迴圈都代表主字串的一位,並且最後給next[q]賦值了,說明每一次迴圈,得到乙個next_table的值,這樣便很自然的明白,for迴圈裡面的while迴圈和if語句都是用來操作k的值,得到「若某一位不匹配,則下一次模式串回溯的目標位置」(先看懂個大概,具體先不細究)

然後看while迴圈和if語句,發現條件中都是在比較k + 1位和q位是否相同(先不看k > -1,也別管whileif的區別)

剛開始k = -1,k + 1 == 0p == 1意思是第0位和第1位比,看是否匹配,假設它從始至終一直都匹配,那麼k和p不斷增大,這過程中next[p]也在不斷增大。

當k等於某個值的時候,發現k + 1與p不匹配了,這時候就執行while語句:回溯。

先枯燥的貼上**:

int

kmp(

char

*str,

int slen,

char

*ptr,

int plen)

}return-1

;}

下面有乙個小故事,可能可以更好的理解回溯:

國慶節的最後一天假期,小明肝了多天遊戲,終於在今晚,他想起了自己還有大把的國慶作業沒有寫,隨後,連續通宵七天的他心想:」唉,肯定做不完作業了,做幾道題目就好睡覺了,身體要緊!「,於是他就寫了前兩道選擇題,之後由於太過興奮,又通宵打了乙個晚上的遊戲。

時間來到了第二天,小明揹著書包來到教室,聽著學霸a裝x:「我第一天就把第三題寫完了」,學霸b裝x:「我第一天就把第四題寫完了」,學霸c也來裝x:「我第一天就把第五題寫完了」……最後留著乙個只寫了前兩道題目的小明在角落裡瑟瑟發抖。

「叮鈴鈴!」鈴聲響了,老師請同學們回答一下作業的答案:老師先抽中了小明,讓他回答,第一題(p == 1),小明回答(k + 1)得很順利(原字串和模式串的第一位匹配),第二題同理,但是第三題,由於小明沒有寫,就亂說了乙個答案,結果當然和老師的答案(原字串的第三位)不匹配。於是老師給小明乙個機會,讓他選乙個人幫他回答,小明記得之前學霸們的裝x言論,於是就求助學霸a(這裡就是k = next[k]的過程,從字尾找字首老師的指標是第三題,學霸a只要從第三題開始就行了,那麼為了k + 1 = 3,那麼k就是2,意思就是學霸a的第三題和老師的指標——第三題答案進行匹配,如果這裡是bf演算法,那麼就一位這老師要從第一題開始問學霸,這個效率就低啦~)

假如說,學霸a也沒有做的話,就再進行回溯(這就是為什麼用while而不是if),如果某一道題,所有裝x學霸都沒有做的話,那麼最後k就會回溯到-1位置,可以理解為最後有一位救世主學神,當然如果學神也匹配不了老師的正確答案,那老師迫不得已只能進行下一題了,也就是指標指向下一題

學霸的第三題答案與老師的第三題指標匹配正確之後,老師的指標指向了下一題——第四題,然而,學霸a並沒有做第四題,這時候老師又給出了一次求助機會,學霸a又想起了之前裝x的學霸b,於是向他求助(同理,這裡也是k = next[k]的過程,學霸b不用從第一題開始回答~)

於是,學霸們和小明互相幫助,遇到沒有做的題目,就求助於之前記憶中那個寫過提愛裝x的學霸來答本題(字首的末尾湊上到老師的指標上)

因為自己沒有完成全部題目,學霸們都秀紅了臉。這天以後,小明和班裡的學霸們不再熱衷於裝逼,形成了商業互吹的良好班風。

KMP演算法求next陣列的一些理解

看了一天的kmp演算法,終於對其有所理解,下面是對該演算法的理解,希望對初學該演算法的同學有所幫助。net函式的求解 先看演算法 public static int getnext int p else return next 對於這個演算法的難點在於,如果對於下乙個next值 其中 p i p j...

對KMP演算法的理解

kmp演算法是一種高效的模式匹配演算法,複雜度可以達到o m n 而普通模式匹配演算法的複雜度為o m n 普通模式匹配演算法 從主串的第乙個字元 或者給定的第pos個字元 開始和子串的第乙個字元開始比較,若相等,則繼續比較後面的字元。若不相等,則從主串本次開始比較的字元的下乙個字元開始,與子串的第...

對KMP演算法的理解

kmp演算法是一種高效的模式匹配演算法,複雜度可以達到o m n 而普通模式匹配演算法的複雜度為o m n 普通模式匹配演算法 從主串的第乙個字元 或者給定的第pos個字元 開始和子串的第乙個字元開始比較,若相等,則繼續比較後面的字元。若不相等,則從主串本次開始比較的字元的下乙個字元開始,與子串的第...