KMP算法

問題描述

KMP算法是用與字符串匹配的算法幢泼,給定文本串,在文本串中尋找模式串征唬,如果找到匹配的模式串便返回文本串首次出現(xiàn)模式串的首字符的地址

算法分析

1th

最簡單最暴力的算法便是逐一匹配全部字串格嘁,該算法的時(shí)間復(fù)雜度為O(m*n),其中m為文本字符數(shù)奏窑,n為字串?dāng)?shù)

思路如圖示:


image.png

遇到匹配失敗后


image.png

逐一遍歷文本串,來匹配模式串屈扎,直至匹配成功埃唯,或者匹配到最后無可匹配

Code

        char text[m];
        char pattern[n];
        for ( i = 0; i < m; i++) {
            for ( j = 0; j < n && (i + j) < m; j++) {
                if (pattern[j] != test[j + n]) {
                    break;
                }
            }
            if (j == n) {
                return true;
            }
        }
        return false;                          

2th

為什么1th版本會(huì)很慢?是因?yàn)樽隽撕芏嗟臒o效匹配鹰晨,以上面的例子為例墨叛,當(dāng)遍歷到text[3]時(shí)匹配失敗止毕,指針退回了text[1]。每一次失敗的匹配只會(huì)重新在下一位繼續(xù)匹配漠趁。從上述例子可以看出從text[1]繼續(xù)下去匹配只會(huì)還是失敗扁凛。這便是為什么慢的原因

這里事先說明,這里所說的指針不是C語言中的指針闯传,是更范的概念谨朝,表示所指元素的地址(不一定是計(jì)算機(jī)內(nèi)部的地址,可以是數(shù)組下標(biāo)

i為模式串的指針
j為文本串的指針

KMP的算法便做了一個(gè)優(yōu)化甥绿,i不會(huì)回退字币,每一次遍歷只會(huì)向前,不會(huì)像前面那樣妹窖,從text[3]的匹配失敗回退到text[1]。

如圖所示:
匹配失敗


image.png

回退之后收叶,匹配成功:


image.png

再次失斀竞簟:


image.png

回退之后,再次失敗判没,但是無法回退了:


image.png

直到最后匹配成功或者失敗


image.png

總結(jié)一下圖中所示:遍歷文本串的指針是不會(huì)后退的蜓萄,只會(huì)一直向前。會(huì)后退的是模式串的指針

每次pattern[i]匹配失敗澄峰,對(duì)應(yīng)的文本串的 j 指針便會(huì)回退到一個(gè)合適的位置(什么是合適的位置嫉沽?后面將會(huì)詳細(xì)的講解)然后從這個(gè)位置再繼續(xù)匹配,如果仍舊匹配失敗俏竞,繼續(xù)回退绸硕,直到匹配成功或者不能再回退為止。

無論最后合適的位置是否匹配魂毁,i 都會(huì)向前

Code

bool KMP(char text[], char pattern[]) {
    int n = strlen(text);
    int m = strlen(pattern);
    getNext(pattern, m);
    int j = -1;
    for (int i = 0; i < n; i++) {
        while (j != -1 && text[i] != pattern[j + 1]){//尋找合適位置玻佩,直到匹配成功或者無可回退
            j = next[j];
        }
        if (text[i] == pattern[j + 1]) {//在pattern[i]匹配成功,移動(dòng) j
            j++;
        }
        if (j == m - 1) {//表示匹配成功
            return true;
        }
    }
    return false;
}

注意上面text[j + 1]才是和pattern[i]相匹配的, j 是相匹配下標(biāo)的前一位
j = next[j];便代表 j 的回退席楚,而next數(shù)組正是管理著文本串每個(gè)字符的回退下標(biāo)咬崔,也就是所謂的合適位置

下面開始講解Next數(shù)組

Next數(shù)組

假設(shè)一個(gè)字符串s和一個(gè)next數(shù)組(長度相等且為l)
next[k](k <= l )里的值等于s的子串n[0....k]的最長相等前后綴的前綴最后一個(gè)元素的下標(biāo),如果找不到相等的前后綴烦秩,便取-1垮斯;

如圖所示:


image.png

字符字串n = abcab的最長前后綴為ab,所以next[4] = 1

而整個(gè)next數(shù)組里存放的值便是字符串s(長度為n)的字符字串[0....k](k <= n)的最長相等前后綴的前綴最后一個(gè)元素的下標(biāo)只祠,如果沒有相等的前后綴兜蠕,便設(shè)為-1

也許有人還不明白這最長前后綴的作用,那么便解釋一下:
當(dāng)pattern[i] != text[j+1]時(shí)抛寝,我們便找到字符串k[0...j]的最長前綴的下標(biāo)牺氨,從該最長前綴繼續(xù)往下匹配(通過更新j來繼續(xù)匹配 j = next[j])*狡耻,如果還不匹配再找該前綴的最長前綴的下標(biāo),直到找到可以匹配或者j = -1

那么如何求出next數(shù)組的值呢猴凹?
假設(shè)我們有一個(gè)字符串s[0...k]夷狰,并且已知next[0....k-1]的值,現(xiàn)在我們要求next[k]

那么我們?cè)趺辞竽亟荐恳驗(yàn)槲覀冇辛俗址畁[0....k-1]相對(duì)應(yīng)的next[]值沼头,所以我們就可以通過比較s[k]是否等于n[0...k-1]的最長子綴的后一位字符來確定next[k]!,如果相同意味著next[k] = next[k - 1] + 1书劝,如果不相同进倍,便找m[0...next[k - 1]]的后一位元素相比較,因?yàn)閱栴}條件中next[0...k-1]我們都是知道的购对!猾昆,一直往下遞歸,直到找到有個(gè)前綴的后一個(gè)元素與之相同(這個(gè)就是s[0...k]的最大前綴骡苞,仔細(xì)想想)或者沒有前綴與之相等(設(shè)為-1)

Notice垂蜗!next[n]里的的值代表字符串m[0...n]的最長前綴的最后一位元素的下標(biāo),不要弄暈了

好了解幽,求法我們已經(jīng)完成了一大半贴见。如果我們有字符串s[0...k+1], 并且已知next[0...k]的值,然后求next[k+1]躲株,這個(gè)過程是否似曾相識(shí)呢片部?

我們只需設(shè)定next[0] = -1;便可以一直往下推出剩下的next值(假定只有一個(gè)字符的串沒有相等的前后綴)

Code

void getNext(char s[], int length) {
    int j = -1;
    next[0] = j;
    for (int i = 1; i < length; i++) {
        while (j != -1 && s[i] != s[j + 1]) {//不退ǎ回退档悠,直到找到或者等于j = -1 
            j = next[j];
        }
        if (s[i] == s[j + 1])//找到
            j++;
        next[i] = j;
    }
}

next數(shù)組和kmp結(jié)合起來一看,便是當(dāng)k+1匹配失敗之后望浩,k便回退到next[k]繼續(xù)匹配站粟,直到可以匹配或者k = -1重新匹配

next找到最大前綴,kmp使用最大前綴來實(shí)現(xiàn) i 一直向前遍歷

nextval數(shù)組

nextval是next的高配版曾雕,next數(shù)組存在多次更新情況奴烙,也就是可能回退多次才能找到最終合適的位置,而nextval一步到位直接找到最合適的位置

s[0...4] = ababa 最大前綴為aba aba的最大前綴為a

如果s的[5] = b,如果匹配失敗剖张,便會(huì)繼續(xù)匹配s[3], s[1]而它們也正好都是b切诀,因此多了幾次無謂的比較
所以一旦s[5]比較失敗,便直接回退到-1搔弄,也就是其該字串的前綴之后的第一個(gè)元素不等于該字串的之后的第一個(gè)元素的最大前綴

只需添加s[j + 1] == s[i +1]這一判斷條件
如果成立幅虑,nextval[j] = nextval[i];
否則,nextval[j] = i;

Summary

總的來說顾犹,KMP是利用了最大前綴來節(jié)省了時(shí)間倒庵,保證pattern一直向前遍歷褒墨,而且text串也最大程度地節(jié)省了匹配位數(shù)
KMP算法是AC自動(dòng)機(jī)的特殊情況,有興趣可以閱讀之后AC自動(dòng)機(jī)講解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末擎宝,一起剝皮案震驚了整個(gè)濱河市郁妈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绍申,老刑警劉巖噩咪,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異极阅,居然都是意外死亡胃碾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門筋搏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仆百,“玉大人,你說我怎么就攤上這事奔脐《碇埽” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵帖族,是天一觀的道長栈源。 經(jīng)常有香客問我挡爵,道長竖般,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任茶鹃,我火速辦了婚禮涣雕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘闭翩。我一直安慰自己挣郭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布疗韵。 她就那樣靜靜地躺著兑障,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蕉汪。 梳的紋絲不亂的頭發(fā)上流译,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音者疤,去河邊找鬼福澡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛驹马,可吹牛的內(nèi)容都是我干的革砸。 我是一名探鬼主播除秀,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼算利!你這毒婦竟也來了册踩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤笔时,失蹤者是張志新(化名)和其女友劉穎棍好,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體允耿,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡借笙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了较锡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片业稼。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚂蕴,靈堂內(nèi)的尸體忽然破棺而出低散,到底是詐尸還是另有隱情,我是刑警寧澤骡楼,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布熔号,位于F島的核電站,受9級(jí)特大地震影響鸟整,放射性物質(zhì)發(fā)生泄漏引镊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一篮条、第九天 我趴在偏房一處隱蔽的房頂上張望弟头。 院中可真熱鬧,春花似錦涉茧、人聲如沸赴恨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伦连。三九已至,卻和暖如春钳垮,著一層夾襖步出監(jiān)牢的瞬間惑淳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工扔枫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汛聚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓短荐,卻偏偏與公主長得像倚舀,于是被迫代替她去往敵國和親叹哭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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

  • 文章大綱:1.KMP算法概念2.KMP算法中最核心的next[] 數(shù)組是如何生成的3.使用KMP算法 匹配字符串 ...
    檸檬烏冬面閱讀 812評(píng)論 0 3
  • 字符串匹配KMP算法詳解 1. 引言 以前看過很多次KMP算法痕貌,一直覺得很有用风罩,但都沒有搞明白,一方面是網(wǎng)上很少有...
    張晨輝Allen閱讀 2,390評(píng)論 0 3
  • 數(shù)據(jù)結(jié)構(gòu) 第8講 KMP算法 講這個(gè)算法之前舵稠,我們首先了解幾個(gè)概念: 串:又稱字符串超升,是由零個(gè)或多個(gè)字符組成的有限...
    rainchxy閱讀 1,279評(píng)論 0 3
  • 1.KMP算法是什么? 1.1 KMP算法求解什么類型問題 字符串匹配哺徊。給你兩個(gè)字符串室琢,尋找其中一個(gè)字符串是否包含...
    mirqzb閱讀 1,515評(píng)論 0 1
  • 數(shù)據(jù)結(jié)構(gòu)與算法--KMP算法查找子字符串 部分內(nèi)容和圖片來自這三篇文章: 這篇文章、這篇文章落追、還有這篇他們寫得非常...
    sunhaiyu閱讀 1,731評(píng)論 1 21