日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

算法 - KMP算法原理顿悟有感

發(fā)布時(shí)間:2024/1/18 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法 - KMP算法原理顿悟有感 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

算法 - KMP算法原理頓悟有感

    • KMP?
    • KMP核心思想
      • 舉個(gè)栗子
    • 上點(diǎn)代碼
      • next數(shù)組
        • (1)若P~j~ == P~t~
        • (2) 若P~j~ 和 P~t~不相等
    • 改進(jìn)上面的KMP算法
      • nextval
        • 求解nextval數(shù)組的一般方法
        • 上代碼

中國(guó)開(kāi)國(guó)七十一年三月十二日下午,解衣欲睡,雨聲入耳,悟以往,思來(lái)日,懼無(wú)以為生計(jì),遂至嗶哩嗶哩尋KMP算術(shù),觀后遂頓悟,以作文記之。

?

KMP?

? KMP算法是在某一字符串中尋找給定子串的算法,比起逐一比較的方法,KMP算法快了不少。

?

KMP核心思想

? 比較過(guò)程如圖所示:

? 此處的疑點(diǎn):為什么有時(shí)候模式串一次往后移那么多?為什么有時(shí)候模式串又只往后移一格?到底是怎么個(gè)模式串移動(dòng)法?

? 這就是KMP的關(guān)鍵。

? 答案:當(dāng)發(fā)現(xiàn)模式串的第i個(gè)字符匹配錯(cuò)誤時(shí),將模式串向后移動(dòng)N個(gè)位置。此處,N = 模式串第i個(gè)字符前的子串中的長(zhǎng)度 - 模式串第i個(gè)字符前的子串最長(zhǎng)且小于子串本身長(zhǎng)度的公共前后綴的長(zhǎng)度。

一句話總結(jié):讓i之前的公共前綴移動(dòng)到公共后綴的位置上。

由此可知,KMP算法的移動(dòng)主要和模式串有關(guān),和待查找的字符串關(guān)系不大。
?

舉個(gè)栗子

假設(shè)模式串為 ABABAAABABAA

假設(shè)模式串與帶比較字符串的第1位比較不匹配:則模式串前移1位(1號(hào)位與主串下一位比較)

假設(shè)模式串與帶比較字符串的第2位比較不匹配:二號(hào)位前的子串為A,公共前后綴長(zhǎng)度是0,則模式串向前移1位(1號(hào)位與主串當(dāng)前位比較)

假設(shè)模式串與帶比較字符串的第3位比較不匹配:三號(hào)位前的子串為AB,公共前后綴長(zhǎng)度是0,則模式串向前移1位(1號(hào)位與主串當(dāng)前位比較)

假設(shè)模式串與帶比較字符串的第4位比較不匹配:三號(hào)位前的子串為ABA,公共前后綴長(zhǎng)度是1,則模式串向前移2位(2號(hào)位與主串當(dāng)前位比較)

到這為之,如圖所示:

我們得到一個(gè)規(guī)律,如果當(dāng)前子串最大公共前后綴長(zhǎng)度為N,那我們就移動(dòng)模式串使N+1號(hào)位與主串當(dāng)前位比較。

那么以此類推:

我們把第一句話標(biāo)記為0,當(dāng)我們看到0時(shí),將1號(hào)位與主串下一位比較。

并且我們將后面的每一句話的第一個(gè)數(shù)字取出,結(jié)合上面的數(shù)組下標(biāo),將這些數(shù)字放在一個(gè)數(shù)組中,這樣一來(lái),根據(jù)數(shù)組所提供的信息,我們?cè)谀J酱先魏我粋€(gè)位置匹配失敗,就知道下一步該怎么做了:

這個(gè)數(shù)組就是傳說(shuō)中的next數(shù)組

?

上點(diǎn)代碼

? 循序漸進(jìn),若想知道KMP算法的代碼實(shí)現(xiàn)怎么寫(xiě),我們先要知道天真的傻瓜式比較法的代碼怎么寫(xiě)。

//too simple, sometimes naive //假設(shè)字符串位置從1開(kāi)始 int naive(String str, String substr) {int i = 1, j = 1, k = i; //i:主串游標(biāo),j:模式串游標(biāo),k:位置記錄器while (i<=str.length && j<=substr.length){ //i、j都落在各自字符串的范圍內(nèi)if(str.ch[i] == substr.ch[j]){i++;j++;}else {j = 1;i = ++k; //尋找初始比較位置的下一個(gè)位置}}if (j>substr.length) return k;else return -1; }

現(xiàn)在用KMP的思想做一點(diǎn)修改:

int KMP(String str, String substr, int next[]) {int i = 1, j = 1; //KMP不需要回溯,不需要kwhile (i<=str.length && j<=substr.length){ if(j == 0 || str.ch[i] == substr.ch[j]){ //此處注意j=0的處理i++;j++;}else {j = next[j]; //i不需要回溯,j有next數(shù)組指導(dǎo)往哪走 }}if (j>substr.length) return i-substr.length; //計(jì)算首字符存在的位置else return -1; }

而關(guān)鍵在于:next數(shù)組怎么獲得?
?

next數(shù)組

KMP所做的事的聰明之處在于:把之前工作的結(jié)果合理利用起來(lái),減少重復(fù)勞動(dòng)。

求解next數(shù)組需要繼承這一思想。

假設(shè)一段模式串如下圖所示:

P為模式串中的字符,P的下標(biāo)代表每一個(gè)字符的位置,模式串的長(zhǎng)度為m

現(xiàn)在我們把模式串復(fù)制一份,并凸顯出1到t位置上的字符,也就是左端長(zhǎng)度為t的子串

我們將上面Pj-t+1到Pj的子串與下面P1到Pt的子串對(duì)應(yīng)起來(lái),假設(shè)紅色部分完全匹配,黃色部分暫時(shí)不知道,則next[j] = t。

現(xiàn)在我們需要求next[j+1]的值。
?

(1)若Pj == Pt

那么很容易求得next[j] = t + 1 = next[j] + 1
?

(2) 若Pj 和 Pt不相等

當(dāng)Pj 不等于 Pt時(shí),這時(shí)的情況似曾相識(shí):主串某個(gè)位置與模式串某個(gè)位置發(fā)生不匹配的現(xiàn)象。

假如我們把上面的字符串稱為假主串,下面的稱為假模式串:

這不就是熟悉的KMP嗎!

在我們已知next[j]求next[j+1]的情況下時(shí),我們可以使用next[j]之前所有的next數(shù)組

所以當(dāng)若Pj 和 Pt不相等時(shí),則循環(huán)將t賦值為next[t],直到t=0或者滿足(1)為止,當(dāng)t = 0時(shí),next[j+1] = 1。

總結(jié):

我們發(fā)現(xiàn),這種求法天生適合翻譯成代碼:

void getNext(string substr, int next[]) {int t = 0, j = 1;next[1] = 0;while (j<substr.length){if(t == 0 || substr.ch[j] == substr.ch[t]){next[j+1] = t + 1;t++; j++;}else t = next[t];} }

? 在求完next數(shù)組之后,為了理解接下來(lái)的工作,以及不忘初心,我們必須回顧及總結(jié)next數(shù)組的含義到底是什么。

? next[5] = 3意味著5這個(gè)位置之前的子串的公共前后綴長(zhǎng)度為2。

? 一句話:next[i] = j意味著i這個(gè)位置之前的模式串有j-1個(gè)字符是能夠與主串匹配的。
?

改進(jìn)上面的KMP算法

? 這里就讓人頭疼了,本來(lái)上面那個(gè)就不怎么好理解,還要改進(jìn)?

在求解next[j]時(shí),上圖其實(shí)做了很多重復(fù)的工作。

因?yàn)?到4上的字符串相等,因此next[5]直接賦值為0即可。

所以針對(duì)KMP算法的改進(jìn)主要集中在求解next數(shù)組的改進(jìn)上,我們把改進(jìn)之后的數(shù)組稱為nextval數(shù)組。

?

nextval

? nextval的優(yōu)化思路:求解nextval[j]時(shí),移過(guò)來(lái)比較的字符必須與比較過(guò)不符合要求的Pj不相同。

? 如上圖所示:其中P代表模式串中的字符,我們?nèi)粢髇extval[j],需不停將j賦值為next[j],再把這些位置上的字符與Pj進(jìn)行比較,如果它們與Pj相等,那么意味著Pj與主串中的字符未匹配,則這些位置上的字符來(lái)到這也沒(méi)用。若其中有一個(gè)字符與Pj不等,那nextval[j]則為對(duì)應(yīng)位置(上圖Pa中的)。

?

求解nextval數(shù)組的一般方法

  • 當(dāng)j等于1時(shí),nextval[j]賦值為0,作為特殊標(biāo)記
  • 當(dāng)j大于1時(shí):
    • 若Pj不等于Pnext[j],則nextval[j]等于next[j]
    • 若Pj等于Pnext[j],則nextval[j]等于nextval[next[j]]
  • ?

    上代碼

    void getNextval(string substr, int nextval[]) {int t = 0, j = 1;nextval[1] = 0; //這句很明顯要加上while (j<substr.length){if(t == 0 || substr.ch[j] == substr.ch[t]){//計(jì)算nextval值if(substr.ch[j+1] != substr.ch[t+1]])nextval[j+1] = t + 1;elsenextval[j+1] = nextval[t + 1];t++; j++;}else t = nextval[t]; //用nextval代替next數(shù)組} }

    總結(jié)

    以上是生活随笔為你收集整理的算法 - KMP算法原理顿悟有感的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。