KMP演算法二

2021-06-08 20:05:54 字數 2811 閱讀 3363

浴火重生大俠的文章我認為是最適合我的理解方式的,不過裡面也有個小錯。

以下紅色部分為個人修改:

其實kmp演算法還是比較容易理解的,關鍵在於next(j)函式容易讓人糊塗,只要把這個函式弄明白了,kmp算也就可以說是真正掌握了。

(說得太對了,next(j)的意思其實就是s在i不回溯的前提下,接下來與p中的第幾位或哪一位去比較)

要說清楚kmp演算法,可以從樸素的模式匹配演算法說起。(樸素,就是最土、最直接、最原始的演算法,就是反映在第個人腦子裡的第一種思路)

樸素的模式匹配演算法比較容易理解,其實現如下:(不得不提的是,2023年7月版的《軟體設計師教程》中該演算法的實現有誤,460頁倒數第三行的「i=1;」應該是「j=0;」才對。害我無端鬱悶了好久)(哎。。這種書怎麼能在**內容上出錯呢,太毀人了。。自己到底有沒有上機試試啊就出版。。)

int index(char s, char p, int pos) //s就是目標串,p就是模式串,就是被查詢與查詢的區別

else  } 

//到這的時候,迴圈已經結束了,我理解,跳出迴圈的原因或者是 i 到了上界(s全找完了,必然是匹配不上,剩餘的長度都不夠了),或者是 j 到了上限,這說明匹配上了)

if(j >= plen) 

else  } 

可見,在樸素的模式匹配演算法中,當模式中的p[j]與主串中的s[i]不匹配時,需要把主串的指標回溯到i-j+1(回退j次成功匹配的單位再向下移一位)的地方從新用s[i-j+1]跟p[0]進行匹配比較。kmp演算法的想法是,能不能不回溯主串的指標呢?這種想法基於如下事實的:p[j]!=s[i]前,p[0]~p[j-1]跟s[i-j]~s[i-1](i-j到i-1顯然就是j個字元哈,p從0到j-1自然也是j個字元)

是匹配的(這裡j>0,也就是說在不匹配前已經有匹配的字元了。否則如果j=0(第一位就沒對上,自然沒有什麼可回溯的哈),則主串指標肯定不用回溯,直接向前變成i+1再跟p[0]比較就是了)

p[j]!=s[i]前,p[0]~p[j-1]跟s[i-j]~s[i-1]是匹配的,這說明了什麼呢?這說明可以通過分析模式的p[0]~p[j-1]來分析s[i-j]~s[i-1]。如果模式中存在p[0]~p[k-1]=p[j-k]~p[j-1](共k個匹配的字元,且k是滿足這個關係的最大值 1到k和0到k-1是相同的位數哦,乙個從後向前數,乙個從前向後數),則可以知道s[i-k]~s[j-1](這裡我怎麼想都應該是s[i-k]~s[i-1])

跟[0]~p[k-1]是匹配的,那麼,s[i]只需要跟p[k]進行比較就行了。而這個k是跟主串無關的,只需要分析模式串就可以求出來(這就是普通的教材中next[j]=k這個假設的由來,普通教材中總喜歡假設這個k值已經有了,如果你邏輯思維強還沒有什麼,不然或多或少會把你卡在這的(我這種邏輯不強的人就被卡在這裡了,書裡就直接給出公式了,也沒說是怎麼來的,其實也並不複雜,但是如果直接就拿來當結論用,如果不能一下想通,怎麼都會覺得突兀……))。亦即next[j]=k。(就是發生不匹配之後,i不回溯的前提下, 下乙個j是哪個位置。。。)

如果上述的p[0]~p[k-1]=p[j-k]~p[j-1]串不存在會怎麼樣呢?這說明p[j]前的串中不存在p[0]...=...p[j-1]的情況,就連p[0]也不等於p[j-1],也就是說p[0]~p[j-1]中所有以p[j-1]為結尾的子串跟模式p都是失配的。基於上面p[0]~p[j-1]=s[i-j]~s[i-1]的事實,可以斷定s[i-j]~s[i-1]中所有以s[i-1]結尾的子串跟模式p都是失配(p相對s向右在1到j個範圍內的移動都是無法匹配的,所以p的頭部直接移到s[i]就行了,前面肯定都對不上)

,這說明把主串的指標回溯到i-j+1~i-1都是沒有必要的,既然沒有必要回溯,而s[i]!=p[j],則s[i]只能跟p[0]進行比較匹配了。亦即next[j]=0。

特殊情況下,j=0,即s[i]!=p[0],這時不用再用s[i]來跟p中的其它字元比較了,變成用s[i+1]跟p[0]進行比較。為了統一,可以讓next[0]=-1。在下一輪的比較中,判斷到j=-1的情況時,讓i=i+1,j=j+1(-1 + 1 = 0),自然就形成s[i+1]跟p[0]比較的效果了。

現在回過頭來看教材上的next[j]的定義

|- -1, 當 j = 0時

next[j]    =    |- max//有的書從0開始,有的書從1開始,不能混。。綠色部分應該去掉吧……

|- 0, 其它情況

是不是很容易理解呢?

時候再來看kmp的演算法實現

int index(char s, char p, int pos, int next)

else //對不上號了,又不是第一位字元 } 

if(j >= plen) 

else  } 

應該不會有什麼疑問了吧?

現在剩下的就是getnext(按照"公式"提前算出來:p中從0到plen-1每乙個位置上的next(j)值的乙個陣列,在上面的**中直接引用)怎麼實現了。

根據上面的推導,知道next[0]=-1。

如果已經知道next[j]=k,

那麼說明p[0]~p[k-1]=p[j-k]~p[j-1],現在next[j+1]應該是多少呢?

若p[k]=p[j],說明p[0]~p[k]=p[j-k]~p[j]成立,則有next[j+1]=k+1。

若p[k]!=p[j],即p[0]~p[k-1]!=p[j-k]~p[j-1],從kmp演算法的角度來看,這時應p[j]應該跟p[next[k]]進行比較。也就是k=next[k],如此重複,由於next[k] 

下面就是getnext函式的實現:(就是模式串如何進行初始化:next[j]為字串0,...,j-1的最長字首的字元個數)

void getnext(char p,int next) 

else  } 

}

演算法 KMP演算法

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

kmp演算法next例題 KMP演算法next陣列求解

kmp演算法與bf演算法的比較 bf演算法的想法十分樸素,即先將子串t的第一位與主串s的第一位對齊開始匹配,當不能匹配時將子串整體往後移一位,然後重新匹配,以此類推直至排出結果 如當遇到下圖所示情況時,需將子串整體後移一位,將i,j分別回溯到主串第2位和子串第一位。kmp演算法 對bf進行思考後,我...

KMP及KMP改進演算法

kmp 看毛片 演算法確實很難理解,上網搜了半天想了很久才大概想明白。kmp演算法精華在於next陣列 部分匹配值 即next陣列就是 字首 和 字尾 的最長的共有元素的長度。以 abcdabd 為例,a 的字首和字尾都為空集,共有元素的長度為0 ab 的字首為 a 字尾為 b 共有元素的長度為0 ...