數(shù)據(jù)結構學習第三彈 串(2) 匹配模式

串的模式匹配

串的模式匹配也可以說子串的定位耙考,是一種重要的串運算荆忍。所謂模式匹配就是給定兩個串s1和s2既穆,在主串s1中找到子串找到一個子串等于s2的過程隐解。對于這樣的運算一般有兩種方法,一種是暴力的匹配方法升酣,而另一種則是KMP算法舷暮。

暴力的匹配方法

int BruteFroce(char s1[],char s2[])
{
    int i=0,j=0;
    int len1 = strlen(s1),len2=strlen(s2);
    while(i<len1 && j<len2)
    {
        //如果當前位置匹配成功,則i和j同時移動
        if(s1[i]==s2[j])
        {
            i++;
            j++;
        }
        else
        {
            //如果不成功噩茄,則i回到匹配前的下一個位置
            i = i-j+1;
            //子串從頭開始匹配
            j = 0;
        }
    }
    //匹配成功放回子串在主串的位置
    if(j==len2)
        return i;
    else    //匹配失敗返回-1
        return -1;
}

從這段代碼可以看得出下面,的確很暴力,逐項進行比較绩聘,對應位置的字符相等就繼續(xù)移動沥割,否則就跳出內層循環(huán)耗啦,讓j回到起點,讓i回到剛剛匹配成功的點的下一個位置驯遇,這種匹配的時間復雜度是(len1*len2)芹彬,然而有一種叫做kmp的算法比他快,時間復雜度是O(len1+len2)叉庐,之所以快的原因是減少了很多在匹配過程中不必要的回退。

kmp算法

next數(shù)組

kmp算法比起直接暴力的之所以快会喝,是因為它沒有像暴力法回退的那么
魯莽(一口氣就跑回去了)陡叠,而kmp先預處理一遍用了一個next數(shù)組
儲存當前位置前綴和后綴(除去本身)最大相等的長度。

那么next存儲了這些數(shù)字有什么用呢肢执?其實當存儲了前綴和后綴最大
相等的長度后有個很好的地方就是枉阵,如果在當前未知匹配失敗了,j是
要往回跑的预茄,但是沒有必要跑到底兴溜,可以根據(jù)next數(shù)組來判斷回到
哪里。

舉個例子

比如一個字符串為"abcaabc"
對于第0個位置來說耻陕,因為除去本身拙徽,所以前綴和后綴的最大長度為0
對于第1個位置來說,因為‘a’和‘b’不相等诗宣,所以前綴和后綴的最大長度為0
對于第2個位置來說膘怕,‘a’和‘c’不相等,‘ab’和'bc'不相等召庞,所以前綴和后綴的最大長度為0
對于第3個位置來說岛心,‘a’和'a'相等,所以前綴和后綴的最大長度為1
對于第4個位置來說篮灼,‘a’和‘a’相等忘古,所以前綴和后綴的最大長度為1
對于第5個位置來說,‘ab’和‘ab’相等诅诱,所以前綴和后綴的最大長度為2
對于第6個位置來說髓堪,‘abc’和‘abc’相等,所以前綴和后綴的最大長度為3

|位置 | 0 | 1 | 2 | 3| 4 | 5 | 6 |
|-------|:-------------------------|
|模式串 | a | b | c | a | a | b | c |
|所以前綴和后綴的最大長度為 | 0 | 0 | 0 | 1 | 1 | 2 | 3 |

而next數(shù)組只需要把上面存儲的值同一向右挪動一個單位逢艘,然后next[0]賦值為-1旦袋,這樣挪移的意思就是不包含當前位置的最大前綴和后綴的長度,所以存儲結果為:

|位置 | 0 | 1 | 2 | 3| 4 | 5 | 6 |
|-------|:-------------------------|
|模式串 | a | b | c | a | a | b | c |
|next數(shù)組 | -1 | 0 | 0 | 0 | 1 | 1 | 2 |

next數(shù)組的求法

那么next數(shù)組怎樣來求呢它改?
其實我們可以這樣來理解疤孕,假設這個next數(shù)組已經寫到了第k位,讓你來寫第k+1位央拖,那么怎么寫呢祭阀,因為第k為記錄的是前k-1個元素最大公共長度鹉戚,那么如果第k個元素等于s[next[k]+1]的話s[k+1] = next[k]+1


代碼實現(xiàn)

#define MAXSIZE 1000
int next[MAXSIZE];
void GetNext(char *s)
{
    next[0] = -1;
    int len = strlen(s);
    int i=0,k=-1;
    while(i<len)
    {
        //s[k]表示前綴,s[i]表示后綴
        if(k==-1 || s[k]==s[i])
        {
            i++;
            k++;
            //如果相等則下一個位置next[i+1]=k+1
            next[i] = k;
        }
        else
            k = next[k];
    }
}

kmp的實現(xiàn)


由圖中可以看出专控,next數(shù)組指引了當前位置不匹配時怎么走下一步抹凳,沒有必要直接讓模式串從頭開始比較
其實kmp主要是next數(shù)組那一部分的理解,如果理解了next數(shù)組和他在kmp中的工作原理伦腐,那么kmp理解就差不多了赢底。

//s1主串,s2模式串
//獲取子串的位置
int kmp(char *s1,char *s2)
{
    //獲取next數(shù)組
    GetNext(s2);
    int i,j=0;
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    while(i<len1 && j<len2)
    {
        //j==-1或匹配成功則繼續(xù)下去
        if(s1[i]==s2[j])
        {
            j++;
            i++;
        }
        else if(j==0)   //如果當前為匹配不成功柏蘑,且j=0幸冻,那么只需要i++就好
            i++;
        else
        {
            //如果j!=0且當前位置匹配不成功j回到next[j]的位置
            j = next[j];
        }
    }
    //匹配成功返回子串位置咳焚,否則返回-1
    if(j==len1)
        return i-j;
    else
        return -1;
}
//用kmp計算子串出現(xiàn)的次數(shù)
int kmp_count(char *s1,char *s2)
{
    //獲取next數(shù)組
    GetNext(s2);
    int i,j=-1洽损,count=0;
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    while(i<len1)
    {
        if(a[i]==b[j])
        {
            i++;
            j++;
        }
        else if(j==0)
            i++;
        else
            j = nex[j];
        if(j==len2)
        {
            count++;
            j = nex[j];     //子串可以相互重疊
            //j=0           計算不重疊子串的數(shù)量
        }
    }
    //返回子串出現(xiàn)的次數(shù)
    return count;
}

kmp練練手

kmp基礎練習1
kmp基礎練習2
kmp基礎練習3

學習當中參考的博客
從頭到尾徹底理解KMP

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市革半,隨后出現(xiàn)的幾起案子碑定,更是在濱河造成了極大的恐慌,老刑警劉巖又官,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件延刘,死亡現(xiàn)場離奇詭異,居然都是意外死亡赏胚,警方通過查閱死者的電腦和手機访娶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來觉阅,“玉大人崖疤,你說我怎么就攤上這事〉溆拢” “怎么了劫哼?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長割笙。 經常有香客問我权烧,道長,這世上最難降的妖魔是什么伤溉? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任般码,我火速辦了婚禮,結果婚禮上乱顾,老公的妹妹穿的比我還像新娘板祝。我一直安慰自己,他們只是感情好走净,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布券时。 她就那樣靜靜地躺著孤里,像睡著了一般。 火紅的嫁衣襯著肌膚如雪橘洞。 梳的紋絲不亂的頭發(fā)上捌袜,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音炸枣,去河邊找鬼虏等。 笑死,一個胖子當著我的面吹牛适肠,可吹牛的內容都是我干的博其。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼迂猴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了背伴?” 一聲冷哼從身側響起沸毁,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎傻寂,沒想到半個月后息尺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡疾掰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年搂誉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片静檬。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡炭懊,死狀恐怖,靈堂內的尸體忽然破棺而出拂檩,到底是詐尸還是另有隱情侮腹,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布稻励,位于F島的核電站父阻,受9級特大地震影響,放射性物質發(fā)生泄漏望抽。R本人自食惡果不足惜加矛,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望煤篙。 院中可真熱鬧斟览,春花似錦、人聲如沸舰蟆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至味悄,卻和暖如春草戈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侍瑟。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工唐片, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涨颜。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓费韭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庭瑰。 傳聞我的和親對象是個殘疾皇子星持,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容

  • 數(shù)據(jù)結構與算法--KMP算法查找子字符串 部分內容和圖片來自這三篇文章: 這篇文章、這篇文章弹灭、還有這篇他們寫得非常...
    sunhaiyu閱讀 1,723評論 1 21
  • 數(shù)據(jù)結構 第8講 KMP算法 講這個算法之前督暂,我們首先了解幾個概念: 串:又稱字符串,是由零個或多個字符組成的有限...
    rainchxy閱讀 1,268評論 0 3
  • 引言 字符串匹配一直是計算機科學領域研究和應用的熱門領域穷吮,算法的改進研究一直是一個十分困難的課題逻翁。作為字符串匹配中...
    潮汐行者閱讀 1,624評論 2 6
  • 概述:本文主要在代碼層面上分析KMP的實現(xiàn)過程,如果您還不了解KMP的推導過程,請參考KMP(一) 模式匹配算法推...
    hehtao閱讀 2,475評論 1 4
  • 一、心是什么捡鱼? 心八回,是核心,是中心驾诈,是內心缠诅,也是自然律動的心。更重要的是翘鸭,心本來是平衡的滴铅,但是心會動,所以會失衡就乓。...
    博川先生閱讀 226評論 0 0