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

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

生活随笔

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

编程问答

【模式匹配】KMP算法的来龙去脉

發(fā)布時(shí)間:2025/3/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【模式匹配】KMP算法的来龙去脉 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 引言

字符串匹配是極為常見(jiàn)的一種模式匹配。簡(jiǎn)單地說(shuō),就是判斷主串T中是否出現(xiàn)該模式串P,即P為T(mén)的子串。特別地,定義主串為T(mén)[0…n?1],模式串為P[0…p?1],則主串與模式串的長(zhǎng)度各為n與p。

暴力匹配

暴力匹配方法的思想非常樸素:

  • 依次從主串的首字符開(kāi)始,與模式串逐一進(jìn)行匹配;
  • 遇到失配時(shí),則移到主串的第二個(gè)字符,將其與模式串首字符比較,逐一進(jìn)行匹配;
  • 重復(fù)上述步驟,直至能匹配上,或剩下主串的長(zhǎng)度不足以進(jìn)行匹配。
  • 下圖給出了暴力匹配的例子,主串T="ababcabcacbab",模式串P="abcac",第一次匹配:

    第二次匹配:

    第三次匹配:

    C代碼實(shí)現(xiàn):

    int brute_force_match(char *t, char *p) {int i, j, tem;int tlen = strlen(t), plen = strlen(p);for(i = 0, j = 0; i <= tlen - plen; i++, j = 0) {tem = i;while(t[tem] == p[j] & j < plen) {tem++;j++;}// matchedif(j == plen) {return i;}}// [p] is not a substring of [t]return -1; }

    時(shí)間復(fù)雜度:i在主串移動(dòng)次數(shù)(外層的for循環(huán))有n?p次,在失配時(shí)j移動(dòng)次數(shù)最多有p?1次(最壞情況下);因此,復(fù)雜度為O(n?p)。


    我們仔細(xì)觀(guān)察暴力匹配方法,發(fā)現(xiàn):失配后下一次匹配,

    • 主串的起始位置 = 上一輪匹配的起始位置 + 1;
    • 模式串的起始位置 = 首字符P[0]。

    如此未能利用已經(jīng)匹配上的字符的信息,造成了重復(fù)匹配。舉個(gè)例子,比如:第一次匹配失敗時(shí),主串、模式串失配位置的字符分別為?a?與?c,下一次匹配時(shí)主串、模式串的起始位置分別為T(mén)[1]與P[0];而在模式串中c之前是ab,未有重復(fù)字符結(jié)構(gòu),因此T[1]與P[0]肯定不能匹配上,這樣造成了重復(fù)匹配。直觀(guān)上,下一次的匹配應(yīng)從T[2]與P[0]開(kāi)始。

    2. KMP算法

    KMP思想

    根據(jù)暴力方法的缺點(diǎn),而引出KMP算法的思想。首先,一般化匹配失敗,如下圖所示:

    在暴力匹配方法中,下一次匹配開(kāi)始時(shí),主串指針會(huì)回溯到i+1,模式串指針會(huì)回退到0。那么,如果不讓主串指針發(fā)生回溯,模式串的指針應(yīng)回退到哪個(gè)位置才能保證正確匹配呢?首先,我們從上圖中可以得到已匹配上的字符:

    T[i…i+j?1]=P[0…j?1]

    ?

    KMP算法思想便是利用已經(jīng)匹配上的字符信息,使得模式串的指針回退的字符位置能將主串與模式串已經(jīng)匹配上的字符結(jié)構(gòu)重新對(duì)齊。當(dāng)有重復(fù)字符結(jié)構(gòu)時(shí),下一次匹配如下圖所示:

    從圖中可以看出,下一次匹配開(kāi)始時(shí),主串指針在失配位置i+j,模式串指針回退到m+1;模式串的重復(fù)字符結(jié)構(gòu):

    T[i+j?m?1…i+j?1]=P[j?m?1…j?1]=P[0…m]

    ?

    且有

    T[i+j]≠P[j]≠P[m+1]

    ?

    那么應(yīng)如何選取m值呢?假定有滿(mǎn)足式子(1)的兩個(gè)值m1>m2,如下圖所示:

    如果選取m=m2,則會(huì)丟失m=m1的這一種字符匹配情況。由數(shù)學(xué)歸納法容易知道,應(yīng)取所有滿(mǎn)足式子(1)中最大的m值。


    KMP算法中每一次的匹配,

    • 主串的起始位置 = 上一輪匹配的失配位置;
    • 模式串的起始位置 = 重復(fù)字符結(jié)構(gòu)的下一位字符(無(wú)重復(fù)字符結(jié)構(gòu),則模式串的首字符)

    模式串P="abcac"匹配主串T="ababcabcacbab"的KMP過(guò)程如下圖:

    部分匹配函數(shù)

    根據(jù)上面的討論,我們定義部分匹配函數(shù)(Partial Match,在數(shù)據(jù)結(jié)構(gòu)書(shū)[2]稱(chēng)之為失配函數(shù)):

    f(j)={max{m}P[0…m]=P[j?m…j],0≤m<j?1else

    ?

    其表示字符串P[0…j]的前綴與后綴完全匹配的最大長(zhǎng)度,也表示了模式串中重復(fù)字符結(jié)構(gòu)信息。KMP中大名鼎鼎的next[j]函數(shù)表示對(duì)于模式串失配位置j+1,下一輪匹配時(shí)模式串的起始位置(即對(duì)齊于主串的失配位置);則

    next[j]=f(j)+1

    ?

    如何計(jì)算部分匹配函數(shù)呢?首先來(lái)看一個(gè)例子,模式串P="ababababca"的部分匹配函數(shù)與next函數(shù)如下:

    j0123456789?
    P[j]ababababca?
    f(j)-1-1012345-10?
    next[j]0012345601?

    模式串的f(j)滿(mǎn)足P[0…f(j)]=P[j?f(j)…j],在計(jì)算f(j+1)分為兩類(lèi)情況:

    • 若P[j+1]=P[f(j)+1],則有P[0…f(j)+1]=P[j?f(j)…j+1],因此f(j+1)=f(j)+1。
    • 若P[j+1]≠P[f(j)+1],則要從P[0…f(j)]中找出滿(mǎn)足P[f(j+1)]=P[j+1]的f(j+1),從而得到P[0…f(j+1)]=P[j+1?f(j+1)…j+1]

    其中,根據(jù)f(j)的定義有:

    P[j]=P[f(j)]=P[f(f(j))]=?=P[fk(j)]

    ?

    其中,fk(j)=f(fk?1(j))。通過(guò)上面的例子可知,函數(shù)fk(j)是隨著k遞減的,并最后收斂于-1。此外,P[j]與p[j+1]相鄰;因此若存在P[f(j+1)]=P[j+1],則必有

    f(j+1)=fk(j)+1

    ?

    為了求滿(mǎn)足條件的最大的f(j+1),因fk(j)是隨著k遞減的,故應(yīng)為滿(mǎn)足上式的最小k值。

    綜上,部分匹配函數(shù)的計(jì)算公式如下:

    f(j)={fk(j?1)+1minkP[fk(j?1)+1]=P[j]?1else

    ?

    代碼實(shí)現(xiàn)

    部分匹配函數(shù)(失配函數(shù))的C實(shí)現(xiàn)代碼:

    int *fail(char *p) {int len = strlen(p);int *f = (int *) malloc(len * sizeof(int));f[0] = -1;int i, j;for(j = 1; j < len; j++) {for(i = f[j-1]; ; i = f[i]) {if(p[j] == p[i+1]) {f[j] = i + 1;break;}else if(i == -1) {f[j] = -1;break;}}}return f; }

    KMP的C實(shí)現(xiàn)代碼:

    int kmp(char *t, char *p) {int *f = fail(p);int i, j;for(i = 0, j = 0; i < strlen(t) && j < strlen(p); ) {if(t[i] == p[j]) {i++;j++;}else if(j == 0)i++;elsej = f[j-1] + 1;}return j == strlen(p) ? i - strlen(p) : -1; }

    時(shí)間復(fù)雜度:fail函數(shù)的復(fù)雜度為O(p),kmp函數(shù)的復(fù)雜度為O(n),所以整個(gè)KMP算法的復(fù)雜度為O(n+p)。

    3. 參考資料

    [1] dekai,?Lecture 16: String Matching.
    [2] E. Horowitz, S. Sahni, S. A. Freed, 《Fundamentals of Data Structures in C》.
    [3] Jake Boxer,?The Knuth-Morris-Pratt Algorithm in my own words.

    轉(zhuǎn)載于:https://www.cnblogs.com/yujihaia/p/7410410.html

    總結(jié)

    以上是生活随笔為你收集整理的【模式匹配】KMP算法的来龙去脉的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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