KMP

基本思想

KMP 基本思想:匹配失敗時(shí),設(shè)法利用這個(gè)已知信息,不要把"搜索位置"移回已經(jīng)比較過的位置探熔,繼續(xù)把它向后移亩鬼,移到第一個(gè)可能匹配的位置殖告。

比如:
母串:ABCDABC......
子串:ABCDABD......
當(dāng)?shù)?7 位 C 與 D 不匹配時(shí),根據(jù)已知信息將子串移動(dòng)到有可能可以匹配的位置雳锋,移動(dòng)位數(shù) = 已匹配的字符數(shù) - 對(duì)應(yīng)的部分匹配值黄绩。

搜索詞 A B C D A B D
部分匹配值 0 0 0 0 1 2 0
  • 已匹配的字符數(shù) = 6
  • 對(duì)應(yīng)的部分匹配值 = 2
  • 移動(dòng)位數(shù) = 已匹配的字符數(shù) - 對(duì)應(yīng)的部分匹配值 = 4

部分匹配值

"部分匹配值"就是"前綴"和"后綴"的最長(zhǎng)的共有元素的長(zhǎng)度。

  • "A" 的前綴和后綴都為空集玷过,共有元素的長(zhǎng)度為 0爽丹;
  • "AB" 的前綴為 [A],后綴為 [B]辛蚊,共有元素的長(zhǎng)度為 0粤蝎;
  • "ABC" 的前綴為 [A, AB],后綴為 [BC, C]嚼隘,共有元素的長(zhǎng)度 0诽里;
  • "ABCD" 的前綴為 [A, AB, ABC],后綴為 [BCD, CD, D]飞蛹,共有元素的長(zhǎng)度為 0谤狡;
  • "ABCDA" 的前綴為 [A, AB, ABC, ABCD],后綴為 [BCDA, CDA, DA, A]卧檐,共有元素為 "A"墓懂,長(zhǎng)度為 1;
  • "ABCDAB" 的前綴為 [A, AB, ABC, ABCD, ABCDA]霉囚,后綴為 [BCDAB, CDAB, DAB, AB, B]捕仔,共有元素為 "AB",長(zhǎng)度為 2;
  • "ABCDABD" 的前綴為 [A, AB, ABC, ABCD, ABCDA, ABCDAB]榜跌,后綴為 [BCDABD, CDABD, DABD, ABD, BD, D]闪唆,共有元素的長(zhǎng)度為 0。

部分匹配實(shí)質(zhì)

有時(shí)候钓葫,字符串頭部和尾部會(huì)有重復(fù)悄蕾。比如,"ABCDAB" 之中有兩個(gè) "AB"础浮,那么它的"部分匹配值"就是 2("AB" 的長(zhǎng)度)帆调。搜索詞移動(dòng)的時(shí)候,第一個(gè) "AB" 向后移動(dòng) 4 位(字符串長(zhǎng)度 - 部分匹配值)豆同,就可以來到第二個(gè) "AB" 的位置番刊。

部分匹配值的選取實(shí)質(zhì)上是基于貪心思想

移動(dòng)后的母串與子串的匹配部分("ABCDAB")的關(guān)系必須保證:母串的后綴等于子串的前綴,且盡量長(zhǎng)影锈。

比如芹务,母串 ("ABCDABC") 與子串 ("ABCDABD") 在第 7 位不匹配時(shí),要把子串移到第一個(gè)可能匹配的位置鸭廷。在已匹配的 "ABCDAB" 中锄禽,第一個(gè)可能匹配的位置便是第 5 位 "A" 。

第一個(gè)可能匹配的位置實(shí)質(zhì)上就是匹配部分(子串前綴 "ABCDAB")"前綴"和"后綴"的最長(zhǎng)的共有元素的首部靴姿。推導(dǎo)如下:

移動(dòng)后的母串與子串的關(guān)系必須保證:母串的后綴 X 等于子串的前綴 Y,且盡量長(zhǎng) --- (1)
既然屬于匹配部分磁滚,那么母串的該后綴 X 也就等于子串的后綴 Z --- (2)
由 (1) (2) 聯(lián)合可得佛吓,子串的前綴 Y 也就等于子串的后綴 Z
所以,匹配本身與母串無關(guān)垂攘,只與子串有關(guān)

KMP 實(shí)現(xiàn)

設(shè) P[j] 為子串匹配部分 B[1 ... j] 的部分匹配值维雇,則 P[j] 應(yīng)該是所有滿足 B[0 ... P[j] - 1] = B[j - P[j] + 1 ... j] 的最大值。由上面的推導(dǎo)可知晒他,P[j] 與母串 A 無關(guān)吱型,可單純從子串 B 推導(dǎo)而來。

那么陨仅,如何快速地預(yù)處理 P 數(shù)組呢津滞?我們可以通過 P[0],P[1]灼伤,…触徐,P[j - 1] 的值來獲得 P[j] 的值。
對(duì)于 B = "ababacb"狐赡,假如我們已經(jīng)求出了 P[0]撞鹉,P[1],P[2] 和 P[3],看看我們應(yīng)該怎么求出 P[4] 和 P[5]

    0 1 2 3 4 5 6
B = a b a b a c b
P = 0 0 1 2 ? ?

P[3] = 2鸟雏,那么 P[4] 顯然等于 P[3] + 1享郊,因?yàn)橛?P[3] 可以知道,B[0, 1] 已經(jīng)和 B[2, 3] 相等了孝鹊,現(xiàn)在又有 B[2] = B[4]炊琉,所以 P[4] 可以由 P[3] 后面加一個(gè)字符得到。

    0 1 2 3 4 5 6
B = a b a b a c b
P = 0 0 1 2 3 ?

B[ P[4] + 1 ] != B[5] 惶室。那么温自,我們要考慮“退一步”了。我們考慮 P[5] 是否有可能由 P[4] 的情況所包含的子串得到皇钞,即是否 P[5] = P[ P[4] ] + 1悼泌。P[4] = 3 是因?yàn)?B[0 ... 2] 和 B[2 ... 4] 都是 "aba";而 P[2] = 1 則告訴我們夹界,B[0]馆里、B[2] 和 B[4] 都是 "a" 。既然 P[5] 不能由 P[4] 得到可柿,或許可以由 P[2] 得到(如果 B[1] 恰好和 B[5] 相等的話鸠踪,P[5] 就等于 P[2] + 1 了)。顯然复斥,P[5] 也不能通過 P[2] 得到营密,因?yàn)?B[1] != B[5] 。事實(shí)上目锭,這樣一直推到 P[0] 也不行评汰,最后,我們得到痢虹,P[5] = 0 被去。

  • 如果匹配成功,P[j] = P[j - 1] + 1
  • 如果匹配失敗奖唯,j 倒退到 P[j - 1]惨缆,再繼續(xù)匹配,直到 j = 0 為止
// getNext P[i]: 滿足 B[0 ... P[i] - 1] = B[i - P[i] + 1 ... i] 的最大值(即匹配值)丰捷,P[i] = 0 時(shí)坯墨,表示匹配值為 0,不能再倒退
let j = 0, P = [];
P[0] = 0;
for (let i = 1; i < B.length; i ++) {
  while (j && B[j] !== B[i]) j = P[j - 1];
  if (B[i] === B[j]) j ++;
  P[i] = j;
}

// j 表示已經(jīng)匹配的長(zhǎng)度病往,即 A[i - j + 1 ... i] = B[0 ... j - 1]畅蹂,如果匹配失敗,倒退到 P[j - 1]
j = 0;
for (let i = 0; i < A.length; i ++) {
  while (j && A[i] !== B[j]) j = P[j - 1];
  if (A[i] === B[j]) j ++;
  if (j === B.length) {
    console.log('Pattern occurs with shift ', i - B.length + 1);
    j = P[j - 1];
  }
}

時(shí)間復(fù)雜度

KMP 的時(shí)間復(fù)雜度分析可謂攤還分析的典型荣恐。我們從上述程序的 j 值入手液斜。每一次執(zhí)行 while 循環(huán)都會(huì)使 j 減欣巯汀(但不能減成負(fù)的),而另外的改變 j 值的地方只有一行少漆。每次執(zhí)行了這一行臼膏,j 都只能加1;因此示损,整個(gè)過程中 j 最多加了 n 個(gè) 1渗磅。于是,j 最多只有 n 次減小的機(jī)會(huì)(j 值減小的次數(shù)當(dāng)然不能超過 n检访,因?yàn)?j 永遠(yuǎn)是非負(fù)整數(shù))始鱼。這告訴我們,while 循環(huán)總共最多執(zhí)行了 n 次脆贵。按照攤還分析的說法医清,平攤到每次 for 循環(huán)中后,一次 for 循環(huán)的復(fù)雜度為 O(1) 卖氨。整個(gè)過程顯然是 O(n) 的会烙。這樣的分析對(duì)于后面 P 數(shù)組預(yù)處理的過程同樣有效,同樣可以得到預(yù)處理過程的復(fù)雜度為 O(m) 筒捺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柏腻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子系吭,更是在濱河造成了極大的恐慌五嫂,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肯尺,死亡現(xiàn)場(chǎng)離奇詭異贫导,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蟆盹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闺金,“玉大人逾滥,你說我怎么就攤上這事“芷ィ” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)缅叠。 經(jīng)常有香客問我锻离,道長(zhǎng),這世上最難降的妖魔是什么槽棍? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任捉蚤,我火速辦了婚禮抬驴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缆巧。我一直安慰自己布持,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布陕悬。 她就那樣靜靜地躺著题暖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捉超。 梳的紋絲不亂的頭發(fā)上胧卤,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音拼岳,去河邊找鬼枝誊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛裂问,可吹牛的內(nèi)容都是我干的侧啼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼堪簿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼痊乾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起椭更,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤哪审,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后虑瀑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體湿滓,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年舌狗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叽奥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痛侍,死狀恐怖朝氓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情主届,我是刑警寧澤赵哲,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站君丁,受9級(jí)特大地震影響枫夺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绘闷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一橡庞、第九天 我趴在偏房一處隱蔽的房頂上張望较坛。 院中可真熱鬧,春花似錦毙死、人聲如沸燎潮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽确封。三九已至,卻和暖如春再菊,著一層夾襖步出監(jiān)牢的瞬間爪喘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工纠拔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秉剑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓稠诲,卻偏偏與公主長(zhǎng)得像侦鹏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子臀叙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 字符串匹配KMP算法詳解 1. 引言 以前看過很多次KMP算法略水,一直覺得很有用,但都沒有搞明白劝萤,一方面是網(wǎng)上很少有...
    張晨輝Allen閱讀 2,390評(píng)論 0 3
  • 數(shù)據(jù)結(jié)構(gòu)與算法--KMP算法查找子字符串 部分內(nèi)容和圖片來自這三篇文章: 這篇文章渊涝、這篇文章、還有這篇他們寫得非常...
    sunhaiyu閱讀 1,729評(píng)論 1 21
  • 數(shù)據(jù)結(jié)構(gòu) 第8講 KMP算法 講這個(gè)算法之前床嫌,我們首先了解幾個(gè)概念: 串:又稱字符串跨释,是由零個(gè)或多個(gè)字符組成的有限...
    rainchxy閱讀 1,278評(píng)論 0 3
  • title: 串的模式匹配算法之kmptags: 數(shù)據(jù)結(jié)構(gòu)與算法之美author: 辰砂tj 1.引言 首先我們需...
    tojian閱讀 962評(píng)論 0 0
  • 文章大綱:1.KMP算法概念2.KMP算法中最核心的next[] 數(shù)組是如何生成的3.使用KMP算法 匹配字符串 ...
    檸檬烏冬面閱讀 812評(píng)論 0 3