<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          高效"KMP"字符匹配算法就這么簡(jiǎn)單

          共 8716字,需瀏覽 18分鐘

           ·

          2021-03-02 23:07



          1、聊一聊


          上一篇文章


          "暴力"字符匹配算法的C語(yǔ)言實(shí)現(xiàn)



          2、KMP算法介紹

          1

          KMP介紹
          KMP是一種字符匹配算法,為啥叫KMP呢?因?yàn)?/span>是由D.E.Knuth,J.H.Morris和V.R.Pratt大佬提出來(lái)的。那一些小伙伴會(huì)問(wèn)了怎么不叫"DJV算法"呢?因?yàn)槔贤舛际敲衷谇?,姓氏在后?/span>

          說(shuō)實(shí)在的bug菌初次接觸這個(gè)字符匹配算法的時(shí)候也是一臉懵逼,那時(shí)候也是找了很多的資料來(lái)理解、分析和推導(dǎo)等等,很多知識(shí)回頭一看也就那么回事,想不明白那時(shí)候怎么會(huì)覺(jué)得那么復(fù)雜,或許這就是對(duì)未知事物的一種敬畏吧。

          KMP算法說(shuō)到底和暴力字符匹配功能上是一模一樣的,就是查找匹配字符串主字符串中的位置,如果只是應(yīng)用完全可以不用理會(huì)它是怎么實(shí)現(xiàn)的,調(diào)用幾個(gè)函數(shù)->傳遞幾個(gè)參數(shù)->得到結(jié)果,然后記得他比暴力匹配高效一點(diǎn)就行了。好了,本文看來(lái)要收尾了。

          2

          還不能結(jié)束

          其實(shí)我們學(xué)習(xí)理論知識(shí)并不是學(xué)習(xí)知識(shí)本身,而是要學(xué)習(xí)了以后能夠獲得一種解決問(wèn)題的辦法和思路。前面那篇文章為大家講解了暴力匹配,也跟大家在文中留了一個(gè)問(wèn)題,假如沒(méi)有KMP算法我們是否有思路去更好的優(yōu)化匹配效率呢?等思考完后再來(lái)看看KMP是如何處理該問(wèn)題的。

          KMP算法的核心 : 就是盡可能利用更多匹配過(guò)程中的信息來(lái)減少匹配串與主串的匹配次數(shù)從而提高匹配效率。

          3、原理分析

          1

          幾個(gè)實(shí)例分析

          那肯定得把暴力匹配中需要優(yōu)化的匹配過(guò)程拿過(guò)來(lái),如下圖所示:



          匹配完abab以后匹配失敗,按照暴力匹配會(huì)把模式串移動(dòng)一個(gè)字節(jié)繼續(xù)重頭開(kāi)始匹配,然而再次匹配必定不成功,得繼續(xù)把模式串移動(dòng)一個(gè)字節(jié)進(jìn)行匹配。

          前面我們講到KMP算法利用已經(jīng)進(jìn)行過(guò)匹配過(guò)的信息進(jìn)行優(yōu)化匹配,如上圖當(dāng)進(jìn)行第一次匹配以后,我們能夠利用的信息有兩個(gè):1)模式串信息;2)已經(jīng)匹配過(guò)的ababa信息,其他信息暫且還未知。

          那么就簡(jiǎn)單點(diǎn)處理 : 如果已經(jīng)匹配的字符串前兩個(gè)字節(jié)與后面兩個(gè)字節(jié)相等,那么就把模式串移動(dòng)兩個(gè)位,繼續(xù)跟主串比較即可,因此從第三個(gè)字符開(kāi)始進(jìn)行接下來(lái)的匹配,無(wú)需重頭開(kāi)始匹配,最終匹配成功。




              上圖是最簡(jiǎn)單的情形,那么下面我們看另外一種情況:



          按照之前的做法,如果已經(jīng)匹配過(guò)的aaaa中前兩個(gè)字節(jié)等于后兩個(gè)字節(jié),那么模式串應(yīng)該移動(dòng)兩個(gè)字節(jié),然而我們發(fā)現(xiàn)其只需要移動(dòng)一個(gè)字節(jié)就能匹配成功了,看來(lái)僅僅用之前的策略還不夠完善,還得補(bǔ)充其它策略才行。



          2

          總結(jié)規(guī)律獲得算法

          通過(guò)前面兩個(gè)例子了解到,我們只需要通過(guò)一套統(tǒng)一的規(guī)律來(lái)指導(dǎo)第A個(gè)字符匹配不成功以后下一步該對(duì)第B個(gè)字符進(jìn)行匹配即可。于是就形成一張映射表-next數(shù)組表(不用太糾結(jié)名字,主要的意思是下一步該如何動(dòng)作所以叫next)。

          這個(gè)next數(shù)組表是通過(guò)模式串獲得的,首先給大家看看一個(gè)next表:


          分析一下:
          • 1)第一行就是模式串的各個(gè)字符,第二行是數(shù)組的標(biāo)號(hào),因?yàn)榈綍r(shí)候會(huì)定義一個(gè)next[5]數(shù)組存放表格中的信息。

          • 2)"前后綴等"表示的是包含比較失敗字符串在內(nèi)的字符前后綴相等的最大字符長(zhǎng)度,而next值是前后綴向右填充并在前面補(bǔ)-1得到的數(shù)組值。結(jié)合下圖和表格中的數(shù)據(jù)字符對(duì)應(yīng)的next值是對(duì)應(yīng)得上的。



          • 3)那么我們可以檢驗(yàn)一下,字符C比較失敗以后從匹配字符[2]繼續(xù)匹配,即模式串的第三個(gè)字符,跟之前的分析一致。

          • 4)同樣我們來(lái)看看aaaac模式串的next數(shù)組圖:


          • 5)同樣加入我們?cè)诘?個(gè)a匹配失敗和最后一個(gè)c匹配失敗以后如何獲得前后綴的呢?如下圖所示,前面的例子沒(méi)有重疊的前后綴出現(xiàn),不過(guò)這里就出現(xiàn)了重疊的問(wèn)題。


          • 6)很明顯如果字符C匹配失敗,對(duì)應(yīng)的需要從模式串[3]繼續(xù)比較,即模式串的第四個(gè)字符繼續(xù)比較,與前面的分析一致。

          • 7)如果大家還看不懂前后綴,那bug菌再畫(huà)一個(gè)圖,你肯定就懂了:




          4、KMP算法的C實(shí)現(xiàn)


          1

          實(shí)現(xiàn)代碼
          對(duì)于KMP算法的代碼實(shí)現(xiàn)真的非常巧妙,而且特別的簡(jiǎn)短,如下是KMP算法的實(shí)現(xiàn),KMP算法目前也存在比較多的改進(jìn)版本,大家常用的還是如下實(shí)現(xiàn):
            1#include <stdio.h>
          2#include <stdlib.h>
          3#include <string.h>
          4
          5#define NEXT_LEN 6
          6
          7char *Parent = "1234567891212123456789";
          8char *Chind  = "121212"
          9int  next[NEXT_LEN] = {0};
          10
          11/******************************************************
          12 * Fuction:KMP匹配算法查詢(xún)函數(shù) 
          13 * Params : str1:主串;str2:模式串;next:next數(shù)組
          14 *         (公眾號(hào) : 最后一個(gè)bug) 
          15 *****************************************************/

          16char* KMP(const char * str1, const char * str2,int * next) 
          17{
          18    int i = 0
          19    int j = 0;
          20    char * ret = (char*)str1;
          21
          22    while (i < strlen(str1) && j < (int)strlen(str2)) //主串結(jié)束、模式串成功 
          23    {
          24        //j = -1直接到next[0]處理或者字符匹配成功
          25        if ((j == -1)||(str1[i] == str2[j])) 
          26        {
          27            //下一個(gè)字符移動(dòng) 
          28            i++; 
          29            j++;
          30        }
          31        else 
          32        {
          33            //如果匹配不成功通過(guò)j(模式串比較失敗地址)找到next中下一次與主串比較的模式串地址 
          34            j = next[j]; 
          35        }    
          36    }
          37
          38    if (j == strlen(str2))//表示的是模式串全部匹配 
          39    {
          40       return (ret + i - j);
          41    }
          42    else 
          43       return(NULL);
          44}
          45/******************************************************
          46 * Fuction:KMP匹配算法next數(shù)組生成 
          47 * Params : str:模式串;next:next數(shù)組
          48 *         (公眾號(hào) : 最后一個(gè)bug) 
          49 *****************************************************/

          50void getNext(const char * str, int * next)
          51{
          52    next[0] = -1;
          53    int i = 0, j = -1;
          54
          55    while (i < (strlen(str) - 1))
          56    {
          57        if ((j == -1 )||(str[i] == str[j]))//通過(guò)模式串自身對(duì)比獲得next數(shù)組值 
          58        {
          59            ++i;
          60            ++j;
          61            next[i] = j;
          62        }   
          63        else
          64        { 
          65            j = next[j];
          66        }
          67    }
          68}
          69
          70/******************************************************
          71 * Fuction:暴力匹配算法 
          72 * Params :str1:主串;str2:模式串 
          73 *        (公眾號(hào) : 最后一個(gè)bug) 
          74 *****************************************************/

          75char *strstr(const char *str1, const char *str2)
          76{
          77    char *cp = (char*)str1;
          78    char *s1, *s2;
          79
          80    if (!*str2)
          81        return((char *)str1);
          82
          83    while (*cp)
          84    {
          85        s1 = cp;
          86        s2 = (char *)str2;
          87        while (*s1 && *s2 && !(*s1 - *s2))
          88        {
          89            s1++, s2++;
          90        }  
          91
          92        if (!*s2)
          93            return(cp);
          94
          95        cp++;
          96    }
          97    return(NULL);
          98}
          99
          100/******************************************************
          101 * Fuction:對(duì)比主函數(shù) 
          102 * Author : (公眾號(hào) : 最后一個(gè)bug) 
          103 *****************************************************/

          104int main(int argc, char *argv[]) {
          105    int result = 0;
          106    int i = 0;
          107    //獲得KMP的next數(shù)組 
          108    getNext(Chind,next);
          109    for( i = 0; i < NEXT_LEN;i++)
          110    {
          111      printf("Next[%d] = %d\n",i,next[i]);  
          112    }
          113    //進(jìn)行KMP匹配 
          114    result = KMP(Parent,Chind,next)- Parent;
          115    printf("KMP    result = %d\n",result);
          116    //進(jìn)行暴力匹配 
          117    result = strstr(Parent,Chind) - Parent;
          118    printf("strstr result = %d\n",result);
          119
          120    return 0;
          121}


          運(yùn)行結(jié)果如下:



          分析一下:
          • 大家仔細(xì)閱讀了會(huì)發(fā)現(xiàn),其實(shí)求next數(shù)組和KMP匹配算法是非常的相似,KMP算法的關(guān)鍵就是求next數(shù)組,一旦匹配不成功就會(huì)去next數(shù)組中找下一個(gè)模式串的匹配點(diǎn),再次拿著該匹配點(diǎn)與匹配失敗主串進(jìn)行比較.

          • 所以KMP算法的優(yōu)越之處在于不再需要重頭開(kāi)始比較,其主串的比較指針是不會(huì)倒退的。至于兩個(gè)算法的時(shí)間上的復(fù)雜度KMP<strstr,這一部分的推導(dǎo)bug菌就不深入分析了,大家有時(shí)間可以查閱一下相關(guān)資料進(jìn)行進(jìn)一步閱讀和學(xué)習(xí),具體的應(yīng)用bug菌在前面的暴力匹配有跟大家講解過(guò),這里就不在贅述了。



          5、最后小結(jié)


          KMP算法就跟大家介紹到這里了,希望大家能有新的收獲,算法的話大家也不需要死記硬背,基本到處都能夠找到,了解原理并且能夠獲得這種處理問(wèn)題的思維就達(dá)到學(xué)習(xí)的目的了。

           




          推薦閱讀:
          專(zhuān)輯|Linux文章匯總
          專(zhuān)輯|程序人生
          專(zhuān)輯|C語(yǔ)言
          我的知識(shí)小密圈

          關(guān)注公眾號(hào),后臺(tái)回復(fù)「1024」獲取學(xué)習(xí)資料網(wǎng)盤(pán)鏈接。

          歡迎點(diǎn)贊,關(guān)注,轉(zhuǎn)發(fā),在看,您的每一次鼓勵(lì),我都將銘記于心~





          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  卡一卡二无码 | 丝袜美腿av | 天天无码 | 免费成人艹逼无码视频 | 操逼操综合网 |