日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

這是我在網上找的資源的一個總結,會先給出一個我看了覺得還行的關于算法的講解,再配上實現的代碼:
Original author: Bill_Hoo
Original Address:
http://blog.sina.com.cn/s/blog_6cf48afb0100n561.html
Original title: KMP算法詳解——適合初學KMP算法的朋友
節選了原文的一部分,并不和原文完全相同

相信很多人(包括自己)初識KMP算法的時候始終是丈二和尚摸不著頭腦,要么完全不知所云,要么看不懂書上的解釋,要么自己覺得好像心里了解KMP算法的意思,卻說不出個究竟,所謂知其然不知其所以然是也。

經過七八個小時地仔細研究,終于感覺自己能說出其所以然了,又覺得數據結構書上寫得過于簡潔,不易于初學者接受,于是決定把自己的理解拿出來與大家分享,希望能拋磚引玉,這便是Bill寫這篇文章想要得到的最好結果了

---------謹以此文,獻給剛接觸KMP算法的朋友,定有不足之處,望大家指正-----------

【KMP算法簡介】

KMP算法是一種改進后的字符串匹配算法,由D.E.Knuth與V.R.Pratt和J.H.Morris同時發現,因此人們稱它為克努特——莫里斯——普拉特操作(簡稱KMP算法)。通過一個輔助函數實現跳過掃描不必要的目標串字符,以達到優化效果。

【傳統字符串匹配算法的缺憾】

Bill認為,對于一種優化的算法,既要知道優化的細節,也更應該了解它的前身(至于KMP是否基于傳統算法,我不清楚,這里只作語境上的前身),了解是什么原因導致了人們要去優化它,因此加入了這一段:

請看以下傳統字符串匹配的代碼:

void NativeStrMatching( ElemType Target[], ElemType Pattern[] )    
{    
    register int TarLen = 0;   // Length of Target    
    register int PatLen = 0;   // Length of Pattern    
   
    // Compute the length of Pattern    
    while( '' != Pattern[PatLen] )    
       PatLen++;    
   
    while( '' != Target[TarLen] )    
   {    
       int TmpTarLen = TarLen;    
       for(int i=0; i<PatLen; i++)    
       {    
           if( Target[TmpTarLen++] != Pattern[i] )    
               break;    
           if( i == PatLen-1 )    
               cout<<"Native String Matching,pattern occurs with shift "<<TarLen<<endl;    
       }    
       TarLen++;    
   }    
}   

【代碼思想】

傳統匹配思想是,從目標串Target的第一個字符開始掃描,逐一與模式串的對應字符進行匹配,若該組字符匹配,則檢測下一組字符,如遇失配,則退回到Target的第二個字符,重復上述步驟,直到整個Pattern在Target中找到匹配,或者已經掃描完整個目標串也沒能夠完成匹配為止。

這樣的算法理解起來很簡單,實現起來也容易,但是其中包含了過多不必要的操作,也就是在目標串中,有些字符是可以直接跳過,不必檢測的。

不妨假設我們的目標串

Target =  "a b c d e a b c d e a b c d f"

需要匹配的模式串

Pattern = "c d f";

那么當匹配到如下情況時

字符串查找 之 KMP算法

 

由于'e' != 'f',因此失配,那么下次匹配起始位置就是目標串的'd'字符

字符串查找 之 KMP算法

 

我們發現這里照樣失配,直到運行到下述情況

字符串查找 之 KMP算法

 

也就是說,中間的四個字符 d e a b 完全沒有必要檢測,直接跳轉到下一個'c'開始的地方進行檢測

由此可見傳統算法雖然簡單易行,但其中包含了過多的不必要操作,并不能很好地達到實際工作中需要的效率,因此個人認為此方法適合為初識字符串匹配做一個鋪墊作用,有拋磚引玉之意。

說其拋磚引玉并不為過,對KMP算法的理解便可以基于傳統模式串匹配算法進行思考。

【KMP算法的引入】

既然知道了傳統算法的不足之處,就要對癥下藥,優化這個冗余的檢測算法。

KMP算法就能很好地解決這個冗余問題。

其主要思想為:

在失配后,并不簡單地從目標串下一個字符開始新一輪的檢測,而是依據在檢測之前得到的有用信息(稍后詳述),直接跳過不必要的檢測,從而達到一個較高的檢測效率。

如我們的

字符串查找 之 KMP算法

 

當第一次失配后,并不從紅色標記字符'd'開始檢測,而是通過一些有用信息,直接跳過后幾個肯定不可能匹配的冗余字符,而直接讓模式串Pattern從目標串的紅色標記字符'c'開始新一輪的檢測,從而達到了減少循環次數的效果

【KMP算法思想詳述與實現】

前面提到,KMP算法通過一個“有用信息”可以知道目標串中下一個字符是否有必要被檢測,這個“有用信息”就是用所謂的“前綴函數(一般數據結構書中的next函數)”來存儲的。

這個函數能夠反映出現失配情況時,系統應該跳過多少無用字符(也即模式串應該向右滑動多長距離)而進行下一次檢測,在上例中,這個距離為4.

總的來講,KMP算法有2個難點:

  • 一是這個前綴函數的求法。
  • 二是在得到前綴函數之后,怎么運用這個函數所反映的有效信息避免不必要的檢測。

下面分為兩個板塊分別詳述:

【前綴函數的引入及實現】

【前綴函數的引入】

對于前綴函數,先要理解前綴是什么:

簡單地說,如字符串A = "abcde" B = "ab"

那么就稱字符串B為A的前綴,記為B ? A(注意那不是"包含于",Bill把它讀作B前綴于A),說句題外話——"?"這個符號很形象嘛,封了口的這面相當于頭,在頭前面的就是前綴了。

同理可知 C = "e","de" 等都是 A 的后綴,以為C ? A(Bill把它讀作C后綴于A)

理解了什么是前、后綴,就來看看什么是前綴函數:

在這里不打算引用過多的理論來說明,直接引入實例會比較容易理解,看如下示例:

字符串查找 之 KMP算法

 

(下述字符若帶下標,則對應于圖中畫圈字符)
這里模式串 P = “ababaca”,在匹配了 q=5 個字符后失配,因此,下一步就是要考慮將P向右移多少位進行新的一輪匹配檢測。傳統模式中,直接將P右移1位,也就是將P的首字符'a'去和目標串的'b'字符進行檢測,這明顯是多余的。通過我們肉眼的觀察,可以很簡單的知道應該將模式串P右移到下圖'a3'處再開始新一輪的檢測,直接跳過肯定不匹配的字符'b',那么我們“肉眼”觀察的這一結果怎么把它用語言表示出來呢?

字符串查找 之 KMP算法

 

我們的觀察過程是這樣的:
P的前綴"ab"中'a' != 'b',又因該前綴已經匹配了T中對應的"ab",因此,該前綴的字符'a1'肯定不會和T中對應的字串"ab"中的'b'匹配,也就是將P向右滑動一個位移是無意義的。

接下來考察P的前綴"aba",發現該前綴自身的前綴'a1'與自身后綴'a2'相等,"a1 b a2" 已經匹配了T中的"a b a3",因此有 'a2' == 'a3', 故得到 'a1' == 'a3'......

利用此思想,可推知在已經匹配 q=5 個字符的情況下,將P向右移 當且僅當 2個位移時,才能滿足既沒有冗余(如把'a'去和'b'比較),又不會丟失(如把'a1' 直接與 'a4' 開始比較,則丟失了與'a3'的比較)。

而前綴函數就是這樣一種函數,它決定了q與位移的一一對應關系,通過它就可以間接地求得位移s。

通過對各種模式串進行上述分析(大家可以自己多寫幾個模式串出來自己分析理解),發現給定一個匹配字符數 q ,則唯一對應一個有效位移,如上述q=5,則對應位移為2.

這就形成了一一對應關系,而這種唯一的關系就是由前綴函數決定的。

這到底是怎樣的一種關系呢?

通過對諸多模式串實例的研究,我們會找到一個規律(規律的證明及引理詳見《算法導論(第二版)》)。

上例中,P 已經匹配的字符串為"ababa",那么這個字符串中,滿足既是自身真后綴(即不等于自身的后綴),又是自身最長前綴的字符串為"aba",我們設這個特殊字串的長度為L,顯然,L = 3. 故我們要求的 s = q - L = 5 - 3 = 2 ,滿足前述分析。

根據這個規律,即可得到我們要求的有效位移s,等于已經匹配的字符數 q 減去長度 L。
即 s = q - L
因為已經分析得到該關系為一一對應關系,因此用數組來表示該函數是比較恰當的,以數組的下標表示已經匹配的字符數 q,以下標對應的數據存儲 L。

以上是Bill的原文


配上一個網上另外搜羅到的代碼:

char *strstr(char *haystack, char *needle)
{
    int *iNext;
    int iSoulen,iTemplen;
    int i,j;
    iSoulen=strlen(haystack);
    iTemplen=strlen(needle);

    iNext=(int *)malloc(sizeof(iNext)*iTemplen)  ;
    /*這里忘了C里面釋放內存的命令是什么了,*/
    /*使用時在結束處把這個數組釋放掉*/

    {/*GetNext*/
    i=1,j=0;
    if (iTemplen>=0) iNext[0]=-1;
    if (iTemplen>=1) iNext[1]=0;
    while (i<iTemplen)
    {
        if (j==-1||needle[i]==needle[j])
        {
            i++;
            j++;
            if (needle[i]!=needle[j]) iNext[i]=j;
            else iNext[i]=iNext[j];
        }
        else j=iNext[j];
    }
    }/*end of GetNext;*/

    {/*Search;  */
    i=0,j=0;
    while (i<iSoulen&&j<iTemplen)
    {
        if (j==-1||haystack[i]==needle[j])
        {i++;j++;}
        else j=iNext[j];
    }
    if (j>=iTemplen) return &(haystack[i-iTemplen]);
    else return NULL;

    }/*end of Search*/
}

分享到:
標簽:算法 KMP
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定