KMP算法

很有啟發(fā)的幾篇文章:
文章傳送門(mén):
文章一:KMP算法的Next數(shù)組詳解
文章二:從頭到尾徹底理解KMP
文章三:字符串匹配的KMP算法
首先說(shuō)說(shuō)字符串模式匹配問(wèn)題:
問(wèn)題描述:子串的定位操作通常稱(chēng)作串的模式匹配,即查找子串在主串中的位置蔚携。我們把主串稱(chēng)為S数初,子串稱(chēng)作模式串T伤锚,用 i 和 j 指示主串S和模式串T中當(dāng)前正待比較的字符位置羔杨。
算法基本思想:從主串S的第pos個(gè)字符起和模式的第一個(gè)字符比較之徘意,若相等岳服,則繼續(xù)逐個(gè)比較下個(gè)字符(pos值不變);若不相等,從主串的pos++個(gè)字符起再重新和模式串的第一個(gè)字符比較鲸沮。依次類(lèi)推档押,直到模式串T中的每個(gè)字符依次和主串S中的一個(gè)連續(xù)的字符序列相等澳盐,則匹配成功,函數(shù)值為主串里的匹配串第一個(gè)字符在主串中的序號(hào)令宿,若匹配不成功叼耙,函數(shù)值返回0.

上圖:

image

匹配過(guò)程如下:
1:pos=1,i=1;j=1;S[i]==T[j],繼續(xù)比較到i=6,j=6時(shí)S[6]!=T[6]
2:pos=2,i=2;j=1;S[i]!=T[j]
3:pos=3,i=3;j=1;S[i]!=T[j]
4:pos=4,i=4;j=1;S[i]==T[j],i=5;j=2;S[i]==T[j]一直到i=9;j=6時(shí)匹配成功,函數(shù)值為4.

數(shù)據(jù)結(jié)構(gòu)描述:

//求子串位置的定位函數(shù) Index(S,T,pos)
int Index(SString S,SString T,int pos){
    //返回子串T在主串中第pos個(gè)字符之后的位置粒没。若不存在筛婉,則函數(shù)值為0.
    //其中,T非空癞松,1<=pos<=StrLength(S)
    i=pos;j=1;
    while(i<=S[0]&&j<=T[0]){
        if(S[i]==T[j]){//繼續(xù)比較后續(xù)字符
            i++;
            j++;
        }
        else{//指針后退重新開(kāi)始匹配
            i=i-j+2;
            j=1;
        }
    }
    if(j>T[0])return i-T[0];
    else return 0;
}//Index

代碼:

#include<bits/stdc++.h>
using namespace std;
int main(){
    string S,T;//S是主串爽撒,T為模式串
    cin>>S>>T;
    int pos=0,flag=0,ans=-1;//flag==1,則匹配成功
    while(1){
        int i=pos;//i是主串的位置指針
        if(S.size()-i<T.size()){
            break;
        }
        for(int j=0;j<T.size();j++){//j是模式串的位置指針
            if(S[i]==T[j]){
                i++;
                if(j==T.size()-1){
                    flag=1;
                    ans=pos+1;//S下標(biāo)是從零開(kāi)始响蓉,故ans在pos基礎(chǔ)上加1
                    break;
                }
                continue;
            }
            else{
                pos++;
                break;
            }
        }
        if(flag==1){
            break;
        }      
    }
    cout<<ans<<endl;
    return 0;
}

KMP算法介紹:
這個(gè)算法是由高德納沃恩·普拉特在1974年構(gòu)思硕勿,同年詹姆斯·H·莫里斯也獨(dú)立地設(shè)計(jì)出該算法,最終由三人于1977年聯(lián)合發(fā)表枫甲。這種算法被稱(chēng)為 Knuth-Morris-Pratt 操作源武,簡(jiǎn)稱(chēng)為 “KMP算法”,常用于在一個(gè)文本串S內(nèi)查找一個(gè)模式串P 的出現(xiàn)位置想幻。

上圖:

image

kmp過(guò)程
1:i=1;j=1.繼續(xù)逐一比較直到i=6;j=6時(shí)S[i]!=L[j]
2:i=4;j=1.繼續(xù)逐一比較直到i=9;j=6匹配成功

顯然软能,KMP算法改進(jìn)之處在于,每當(dāng)一趟匹配過(guò)程中出現(xiàn)字符比較不等時(shí)举畸,不需回溯i指針查排,而是利用已經(jīng)得到的“部分匹配”的結(jié)果將模式向右”滑動(dòng)“盡可能遠(yuǎn)的一段距離后繼續(xù)進(jìn)行比較。那么問(wèn)題來(lái)了抄沮,模式串向右“滑動(dòng)”的可行距離多遠(yuǎn)跋核,即當(dāng)主串中第 i 個(gè)字符與模式中第 j 個(gè)字符比較不等時(shí)岖瑰,主串中第 i 個(gè)字符應(yīng)與模式串中哪個(gè)字符進(jìn)行比較?(注意, i 不回溯砂代。)

由此引出next數(shù)組:
next[j]=k;則next[j]表明當(dāng)模式串中第j個(gè)字符與主串第i個(gè)字符“失配”時(shí)蹋订,在模式串中需重新和主串中該字符進(jìn)行比較的字符的位置。即j-next[j]就是模式串向右“滑動(dòng)”的距離刻伊。所以問(wèn)題在于如何求next數(shù)組露戒?

引出前綴和后綴:假設(shè)主串's_1s_2...s_n',模式串為'p_1p_2...p_m'捶箱,并假設(shè)此時(shí)應(yīng)與模式串中第k(k<j)個(gè)字符繼續(xù)比較智什,則模式串中前k-1個(gè)字符的子串必須滿(mǎn)足關(guān)系式(4-2),且不可能存在k'>k滿(mǎn)足下列關(guān)系式(4-2)
'p_1p_2...p_{k-1}'='s_{i-k+1}s_{i-k+2}...s_{i-1}' (4-2)
已經(jīng)得到的“部分匹配”的結(jié)果是
'p_{j-k+1}p_{j-k+2}...p_{j-1}'='s_{i-k+1}s_{i-k+2}...s_{i-1}'(4-3)
由式(4-2)和式(4-3)推得下列等式
'p_1p_2...p_{k-1}'='p_{j-k+1}p_{j-k+2}...p_{j-1}'(4-4)
式子(4-4)用文字描述:
模式串中存在兩個(gè)相同的子串,該子串以模式串的第一個(gè)字符為首字符丁屎,長(zhǎng)度為k-1荠锭,k-1就是"前綴"和"后綴"的最長(zhǎng)的共有元素的長(zhǎng)度。"前綴"指除了最后一個(gè)字符以外晨川,一個(gè)字符串的全部頭部組合证九;"后綴"指除了第一個(gè)字符以外,一個(gè)字符串的全部尾部組合共虑。
舉例:

image

- "A"的前綴和后綴都為空集愧怜,共有元素的長(zhǎng)度為0;
- "AB"的前綴為[A]妈拌,后綴為[B]叫搁,共有元素的長(zhǎng)度為0;
- "ABC"的前綴為[A, AB]供炎,后綴為[C,BC],共有元素的長(zhǎng)度0疾党;
- "ABCD"的前綴為[A, AB, ABC]音诫,后綴為[D,CD,BCD],共有元素的長(zhǎng)度為0雪位;
- "ABCDA"的前綴為[A, AB, ABC, ABCD]竭钝,
后綴為[A,DA,CDA,BCDA],共有元素為"A"雹洗,長(zhǎng)度為1香罐;
- "ABCDAB"的前綴為[A, AB, ABC, ABCD, ABCDA],后綴為 [B,AB,DAB,CDAB,BCDAB]时肿,共有元素為"AB"庇茫,長(zhǎng)度為2;
- "ABCDABD"的前綴為[A, AB, ABC, ABCD, ABCDA, ABCDAB]螃成,后綴為[D,BD,ABD,DABD,CDABD,BCDABD]旦签,共有元素的長(zhǎng)度為0查坪。

next數(shù)組的代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100;
int nxt[maxn];
void getNext(string p){
    // 初始條件
    int j=0;
    int k=-1;
    nxt[0]=-1; 
    // 根據(jù)已知的前j位推測(cè)第j+1位
    while(j<p.size()-1){//p[k]表示前綴,p[j]表示后綴
        if(k==-1||p[j]==p[k])nxt[++j]=++k;
        else k=nxt[k];       
    }
}
int main(){
    string p;
    cin>>p;
    getNext(p);
    for(int i=0;i<p.size();i++)
        cout<<nxt[i]<<' ';
    cout<<endl;
    return 0;
}

運(yùn)行:


nxt.png

改進(jìn):

void getNext(string p){
    // 初始條件
    int j=0;
    int k=-1;
    nxt[0]=-1; 
    // 根據(jù)已知的前j位推測(cè)第j+1位
    while(j<p.size()-1){//p[k]表示前綴宁炫,p[j]表示后綴
        if(k==-1||p[j]==p[k]){
            ++j;
            ++k;
            if(p[j]!=p[k])nxt[j]=k;//優(yōu)化
            else nxt[j]=k;
        }
        else k=nxt[k];       
    }
}

改進(jìn)原因:當(dāng)p[j] != s[i] 時(shí)偿曙,下次匹配必然是p[ next [j]] 跟s[i]匹配,如果p[j] = p[ next[j] ]羔巢,必然導(dǎo)致后一步匹配失斖洹(因?yàn)閜[j]已經(jīng)跟s[i]失配,然后你還用跟p[j]等同的值p[next[j]]去跟s[i]匹配竿秆,很顯然启摄,必然失配),所以不能允許p[j] = p[ next[j ]]袍辞。如果出現(xiàn)了p[j] = p[ next[j] ]咋辦呢鞋仍?答案是需要再次遞歸,即令next[j] = next[ next[j] ]搅吁。
如圖:
s_3!=p_3注意 p_1==p_3

image

若不優(yōu)化威创,此時(shí)應(yīng)該找到nxt[j],此時(shí)j==3,
因此此nxt[3]==1;而p[nxt[3]]==b;是無(wú)效操作谎懦。

完整版kmp算法

#include<bits/stdc++.h>
using namespace std;
const int maxn=100;
int nxt[maxn];
string s,p;
void getNext(string p){
    // 初始條件
    int j=0;
    int k=-1;
    nxt[0]=-1; 
    // 根據(jù)已知的前j位推測(cè)第j+1位
    while(j<p.size()-1){//p[k]表示前綴肚豺,p[j]表示后綴
        if(k==-1||p[j]==p[k]){
            ++j;
            ++k;
            if(p[j]!=p[k])nxt[j]=k;//優(yōu)化
            else nxt[j]=k;
        }
        else k=nxt[k];       
    }
}
int kmp(int lens,int lenp){
    int i=0,j=0,ans=-1;//如果最后ans==1,則匹配失敗
    while(i<lens&&j<lenp){
        if(j==-1||s[i]==p[j]){
            i++;
            j++;
        }
        else j=nxt[j];
        if(j==lenp)return ans=i-lenp+1;//習(xí)慣上第一個(gè)字符下標(biāo)為1
    }
    return ans;
}
int main(){
    //string s,p;
    cin>>s>>p;
    memset(nxt,-1,sizeof(nxt));
    getNext(p);
    int lens=s.size();
    int lenp=p.size();
    int ans=kmp(lens,lenp);
    cout<<ans<<endl;
    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市界拦,隨后出現(xiàn)的幾起案子吸申,更是在濱河造成了極大的恐慌,老刑警劉巖享甸,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件截碴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蛉威,警方通過(guò)查閱死者的電腦和手機(jī)日丹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚯嫌,“玉大人哲虾,你說(shuō)我怎么就攤上這事≡袷荆” “怎么了束凑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)栅盲。 經(jīng)常有香客問(wèn)我汪诉,道長(zhǎng),這世上最難降的妖魔是什么谈秫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任摩瞎,我火速辦了婚禮拴签,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旗们。我一直安慰自己蚓哩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布上渴。 她就那樣靜靜地躺著岸梨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪稠氮。 梳的紋絲不亂的頭發(fā)上曹阔,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音隔披,去河邊找鬼赃份。 笑死,一個(gè)胖子當(dāng)著我的面吹牛奢米,可吹牛的內(nèi)容都是我干的抓韩。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鬓长,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谒拴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起涉波,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤英上,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后啤覆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體苍日,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年窗声,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了相恃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫌佑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侨歉,到底是詐尸還是另有隱情屋摇,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布幽邓,位于F島的核電站炮温,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏牵舵。R本人自食惡果不足惜柒啤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一倦挂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧担巩,春花似錦方援、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拳话,卻和暖如春先匪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弃衍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工呀非, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人镜盯。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓岸裙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親形耗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哥桥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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

  • 字符串匹配KMP算法詳解 1. 引言 以前看過(guò)很多次KMP算法,一直覺(jué)得很有用激涤,但都沒(méi)有搞明白拟糕,一方面是網(wǎng)上很少有...
    張晨輝Allen閱讀 2,411評(píng)論 0 3
  • 數(shù)據(jù)結(jié)構(gòu)與算法--KMP算法查找子字符串 部分內(nèi)容和圖片來(lái)自這三篇文章: 這篇文章、這篇文章倦踢、還有這篇他們寫(xiě)得非常...
    sunhaiyu閱讀 1,743評(píng)論 1 21
  • 數(shù)據(jù)結(jié)構(gòu) 第8講 KMP算法 講這個(gè)算法之前送滞,我們首先了解幾個(gè)概念: 串:又稱(chēng)字符串,是由零個(gè)或多個(gè)字符組成的有限...
    rainchxy閱讀 1,310評(píng)論 0 3
  • 文章大綱:1.KMP算法概念2.KMP算法中最核心的next[] 數(shù)組是如何生成的3.使用KMP算法 匹配字符串 ...
    檸檬烏冬面閱讀 818評(píng)論 0 3
  • KMP的由來(lái) 在KMP算法之前,對(duì)文本進(jìn)行匹配時(shí)使用的是樸素模式匹配算法,也就是最簡(jiǎn)單匹配算法.當(dāng)然運(yùn)行效率也是讓...
    圣光懺悔閱讀 1,763評(píng)論 2 13