溫故KMP演算法

2022-01-24 09:39:10 字數 2951 閱讀 7866

最近由於某些原因,又回顧了一次kmp演算法。上一次回顧kmp演算法還是在刷題的時候遇到的:

在我的記憶力,每次回顧kmp演算法都會有新的理解,以為自己理解的很透徹了,等過一段時間再去回顧,又要花一些時間去弄門清。這次也一樣。

剛接觸next陣列的時候我很反感字串字首和字尾的最長公共子串的長度來解釋next陣列,我認為next陣列就是乙個字串的對稱程度。在這樣的理解之下,計算next陣列的理解就是:

在求解next陣列的時候,若前面乙個next數,為0,那麼說明前面沒有對稱的,新加的字元如果要對稱只可能和第乙個字元開始比較。如果next數不為0,說明前面乙個字元是有和它對稱的,那麼去找和他對稱的字元的下乙個字元,如果相等那麼next值就++,如果不相等只能等於0了。

從今天看來,這個對稱理解顯然是錯誤的,很容把誤導到回文串裡面的前後對稱。kmp演算法其實很簡單,就從字首和字尾去理解他,這也是他演算法的核心思想。

下面舉個例子:

第一次匹配:從第0位開始,匹配到第7位都是相同的,最後一位發現不一樣了就是第8位

0   1    2   3   4   5    6   78

a   b   c    x   y    a   b   c   x   y   a-------------目標字串

a   b   c    x   y    a   b   c   1     -----------------模式字串

接下來:

如果是暴力的話,應該是模式字串向前移動一位,進行比較,發現第一位有不匹配的繼續移動。

0   1    2   3   4   5    6   78

a   b   c    x   y    a   b   c   x   y  a-------------目標字串

a   b   c    x    y   a   b   c   1     -----------------模式字串

假設暴力移動了x位,終於有可能匹配了,這裡是有可能。那麼情況一定是這樣:

0   1    2   3   4   5    6   78

a   b   c    x   y    a   b   c   x   y   a-------------目標字串

a   b   c   x   y   a   b   c   1     -----------------模式字串

模式字串的a , b ,c和目標的5,6,7位是相同的,(我們不看第8位以及後面的只看0~7)。這樣才有可能匹配(前面移動的都是從第一位就pass掉了)。

那麼回到第一步:

0   1    2   3   4   5    6   78

a   b   c    x   y    a   b   c   x   y   a-------------目標字串

a   b   c    x   y    a   b   c   1     -----------------模式字串

在發現第8位不匹配的時候,我們之前暴力推算過,向前移動5位,才有可能匹配。(只看0~7位)前7位都是相同的,我們可以找到規律,為什麼移動5位才有可能匹配:

a   b   c   x   y   a   b  c

a   b  c   x    y   a   b  c

可以看這就是乙個字串的字首=字尾的情況,不是嗎?也就是說,只有當字首等於字尾存在的情況下,你往後移才有可能匹配(在0~7之內有匹配的)。在發現第8位不匹配的情況下,我們利用next陣列,直接找到字首=字尾的那部分,直接移動過去,這樣省了很多步暴力。如果發現字首=字尾的情況不存在,那麼好辦,直接跳過0~7位,因為字首=字尾不存在,你在0~7位之間怎麼移動都不可能匹配。

接下來就是利用字首與字尾求next陣列的方法,很容易理解。

比如 s: a   b    a   b 

next[i]  表示的是從第0~i位的字串,字首和字尾的最大公共子串的長度。求解next[i] 其實只有兩種情況,一種是next[i-1]也就是0~i-1的子串存在前字尾最大公共子串,例如a  b  a  b 現在求解最後一位b也就是next[3],可以看next[2]=1 因為a b a的公共前字尾是a長度是1,s[0]=s[2]="a" 。 那麼如果s[1]=s[3]的話,公共前字尾豈不是要加1,於是b就去找s[2]匹配的字首就是s[1],找他的下一位s[1],果然和自己相等,於是在next[2]的基礎上加1.。還要一種就是前面的next[i-1]沒有前字尾公共子串,那麼看來只有從自己開始開闢了,忽視果斷和第一位比較,如果相等,那麼從i開始就有了前字尾公共子串,長度為1.

這裡還要提一點,next[i] 還表示和s[i]相等的字首s[j]的下標j,s[j]是字首的最後乙個字元,s[i]是字尾的最後乙個字元。s[i]=s[j] ,j的值既是下標(從0開始的要加 1)也是長度。

next[0]   a   只有乙個字串,最大公共子串長度為0

next[1]   a b   由於next[0]=0,說明前面的子串沒有前字尾相等的情況,只能從自己開闢,發現s[0]和自己不一樣,於是只能next[1]=0

next[2]  a b a   next[1]=0,同樣的從自己開闢,發現s[0]和自己一樣,終於有戲,於是next[2]=1

next[3]  a b a b    next[2]=1 ,前面有匹配的,於是找到next[2]匹配的那個字串下表也就是next[2]的值,是1(我這裡是下標從0開始)於是找s[0]的下一位s[1]發現和自己一樣,很完美,在next[2]的基礎上加1。如果不一樣呢,那麼很認命,自己破壞了前字尾公共子串,只能是0.

至於**什麼的就不貼了,明白了原理,寫**是信手拈來的事情,對吧!

溫故 LRU演算法和LFU演算法

lru和lfu是不同的 lru是最近最少使用頁面置換演算法 least recently used 也就是首先淘汰最長時間未被使用的頁面 lfu是最近最不常用頁面置換演算法 least frequently used 也就是淘汰一定時期內被訪問次數最少的頁 比如,第二種方法的時期t為10分鐘,如果每...

演算法 KMP演算法

kmp演算法主要解決的問題就是在字串 主串 中的模式 pattern 定位問題。記主串為t,模式串為p,則kmp演算法就是返回p在t 現的具體位置,如果沒有出現則返回 1。如果 i 指標指向的字元和 j 指標指向的字元不一致,那麼把 i 右移1位,j 從0位開始,從新開始匹配 如果 i 指標指向的字...

最短路Dijksta演算法溫故

poj2387 最基本得最短路問題,雙向圖,無負權迴路,可以用來熟悉一下自己得思路,和預處理得過程 include include define inf 0x3f3f3f3f using namespace std const int maxn 1010 int mp maxn maxn int d...