1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        高效"KMP"字符匹配算法就這么簡單

        共 8716字,需瀏覽 18分鐘

         ·

        2021-03-02 23:07



        1、聊一聊


        上一篇文章


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



        2、KMP算法介紹

        1

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

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

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

        2

        還不能結(jié)束

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

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

        3、原理分析

        1

        幾個實(shí)例分析

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



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

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

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




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



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



        2

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

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

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


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

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



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

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


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


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

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




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


        1

        實(shí)現(xiàn)代碼
        對于KMP算法的代碼實(shí)現(xià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匹配算法查詢函數(shù) 
        13 * Params : str1:主串;str2:模式串;next:next數(shù)組
        14 *         (公眾號 : 最后一個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            //下一個字符移動 
        28            i++; 
        29            j++;
        30        }
        31        else 
        32        {
        33            //如果匹配不成功通過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 *         (公眾號 : 最后一個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]))//通過模式串自身對比獲得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 *        (公眾號 : 最后一個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:對比主函數(shù) 
        102 * Author : (公眾號 : 最后一個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ì)閱讀了會發(fā)現(xiàn),其實(shí)求next數(shù)組和KMP匹配算法是非常的相似,KMP算法的關(guān)鍵就是求next數(shù)組,一旦匹配不成功就會去next數(shù)組中找下一個模式串的匹配點(diǎn),再次拿著該匹配點(diǎn)與匹配失敗主串進(jìn)行比較.

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



        5、最后小結(jié)


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

         




        推薦閱讀:
        專輯|Linux文章匯總
        專輯|程序人生
        專輯|C語言
        我的知識小密圈

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

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





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

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费A级毛片无码免费120软件 | 欧美日本午夜久久 | 中国一级特黄老太婆A片 | 日产mv高清直播 | 尤物禁止想象AV | 最近中文字幕免费MV第一季歌词怀孕 | 一插综合网 | 国产精品久久久久久久美女直播 | 蜜桃视频网 | 免费精品﹣色哟哟 |