KMP算法的next函數求解和分析過程


轉自

wang0606120221:http://blog.csdn.net/wang0606120221/article/details/7402688

 

 

假設KMP算法中的模式串為P,主串為S,那么該算法中的核心是計算出模式串的P的next函數。

KMP算法是在已知的模式串的next函數值的基礎上進行匹配的。

由於本次只討論next的求值過程,因此KMP算法的數學推理過程這里不再講解。

從KMP算法的數學推理可知,此next函數只取決與模式匹配串自身的特點和主串沒有任何關系,此函數

默認認為next[1]=0,由於next[j]=k表示的意義是當模式串和主串的第j個字符不匹配時,那么接下來和主串的第j個

字符匹配的字符是模式串的第k個字符。因此,next[1]=0表示當主串的當前字符和模式串的第1個字符不匹配,接

下來需要用模式串的第0個字符和主串的當前字符匹配,由於模式串下標是從1開始的,所以不可能存在第0個字符,

即接下的匹配動作是主串和模式串同時向右移動一位,繼續模式匹配。

例如:主串:a c a b a a b a a b n a c

       模式串:a b a a b

          主串:a c a b a a b a a b n a c

      模式串:   a b a a b

          主串:a c a b a a b a a b n a c

      模式串:      a b a a b

此時,主串和模式串不匹配,而next[1]=0,因此,模式串的第0個字符和主串的第2個字符比較,而模式串沒有第0個

字符,此時可以把第0個字符理解為空字符,即模式串向右移動一位,主串再繼續喝模式串匹配,而此時的主串的當

前字符是第3個字符,整體來看是當主串和模式串的第1個字符不匹配時,主串和模式串同時右移一位,然后繼續匹配。

接下來講解一般情況下的next函數值求解過程。

設next[j]=k,根據KMP算法的模式串的特點可知,‘p1p2......pk-1’=‘pj-k+1......pj-1’,其中k必須滿足1<k<j,並且不可能存在k‘>k滿足上面等式。那么能夠根據next[j]=k計算出next[j+1]的值嗎?顯然是能夠計算出來的,不然我也不廢話了,呵呵。

 

下面討論具體求解過程:

當知道next[j]=k的值,求next[j+1]的值時候有兩種情況,一種是pk=pj,另一種情況是pk!=pj。

1.當pk=pj時,那么可以得出在模式串中存在‘p1......pk’=‘pj-k+1......pj’子串等式。並且不可能存在k‘>k,滿足

"p1......pk’ "=" pj-k’+1......pj "等式(該等式中用的是雙引號,只是為了區別外邊字符串的引號和內部k‘的引號,沒有其它意圖,特此說明),因為根據KMP算法的說明k是最大的。

因此,顯而易見next[j+1]=k+1=next[j]+1。該式表示當主串的當前字符和模式串的第j+1個字符不匹配時,需要使用模式串的第k+1個字符和當前主串字符比較匹配,即主串的當前字符不變,變的只是模式串的字符,可以理解為模式串向右移動j+1-(k+1)=j-k個字符。

2.當pk!=pj時,可知在模式串中不存在‘p1......pk’=‘pj-k+1......pj’等式。表明不能簡單的用模式串的第k+1個字符和主串比較,因為pk!=pj,此時需要把模式串向右移動更多的位數,直到找出模式串的前m個字符和主串中的m個字符匹配為止或者沒有找到這樣的子串,那么意味着模式串需要從第1個字符開始和主串從新匹配。

 

          主串:a c a b a a c a b a a b a a b n a c

      模式串:      a b a a b

          主串:a c a b a a c a b a a b a a b n a c

      模式串:            a b a a b

在此首先給出了next函數的數值,為了方便說明當pk!=pj時,使用pk=pj的方法是不對的。

next[1]=0,next[2]=1,next[3]=1,next[4]=2,next[5]=2.

此時,i=7,j=5,而s[7]!=t[5],如果簡單按照pk=pj時候的方法,用第k+1=2+1=3個字符和主串的第7個字符比較時,顯然不行,因為s[6]=a!=b=t[2]。前面子串都不匹配,那么匹配后面的字符顯然是笑話。說明此時模式串需要向右移動更多的位數,知道找到合適的字符或者沒有找到,需要從第1個字符重新和主串匹配。

 

因為已經知道next[j]=k,因此可知模式串中p1=pj-k+1,p2=pj-k+2,......,pk-1=pj-1,則此時應該將模式串繼續向右移動直到第m+1個字符,該字符滿足’p1......pm‘=’pj-m+1......pj‘,(1<m<k<j)並且不存在m’>m也滿足該等式。此時就可以使用第m+1個字符和當前主串字符比較匹配,(假設當前主串的字符索引是i+1)

此時主串中必存在關系式‘si-m+1......si’=‘pj-m+1......pj’=‘p1......pm’,因此用模式串的第m+1個字符和當前主串字符比較匹配是正確的。此時next[j+1]=m+1=next[......next[next[k]]]+1。

 

這里m=next[......next[next[k]]],這里解釋一下m=next[......next[next[k]]],由於pk!=pj,因此需要向右移動模式串,

首先移動第next[k]個字符和第j個字符比較,假設next[k]=h,如果ph=pj,

則說明模式串中存在等式‘p1......ph’=‘p-h+1......pj’,(1<h<k<j)(假設主串中當前和模式串比較字符是第i+1個)則主串中必然存在‘si-h+1......si’=‘pj-h+1......pj’=‘p1......ph’等式(1<h<k<j)。也就是說next[j+1]=h+1。

                                                          即next[j+1]=next[k]+1。

同理,如果ph!=pj,則模式串還需要繼續向右移動,用第next[h]個字符和第j個字符比較,以此類推,直到第j個字符和模式串中的某個字符匹配成功或者不存在u(1<u<j)滿足等式‘p1......pu’=‘pj-u+1......pj’。如果找到了匹配字符u,那么

next[j+1]=u+1,否則next[j+1]=1。

下面舉例說明該過程:next函數值只與模式串自身的特點有關;

 模式串的索引:j   1 2 3 4 5 6 7 8

           模式串:     a b a a b c a c

           next值:      0 1 1 2 2 3 1 2

默認設置next[1]=0;

計算next[2]:因為p1......p2-1,不可能存在索引k,滿足1<k<2,因此next[2]=1;

計算next[3]:因為p3-1=b!=a=p1,next[2]=1,而next[1]=0,所以不存在u,滿足上述表達式,next[3]=1;

計算next[4]:next[3]=1,p4-1=a=p1,next[4]=next[3]+1=1+1=2;

計算next[5]:next[4]=2,p2=b,p5-1=a!=p2,pj=p4=a,next[2]=1,p1=a=pj,所以next[5]=next[2]+1=1+1=2;

計算next[6]:next[5]=2,p2=b,p6-1=b=p2,next[6]=next[5]+1=2+1=3;

計算next[7]:next[6]=3,p3=a,p7-1=c!=p3,pj=p6=c,next[3]=1,p1=a!=pj,next[1]=0,因此不存在u,所以,next[7]=1;

計算next[8]:next[7]=1,p1=a,p8-1=a=p1,next[8]=next[7]+1=1+1=2。

            

為什么當模式串第j個字符和主串不匹配時接着用第next[k]個字符直接和第j個字符比較,而不是用第j-1個字符和主串比較呢?

我的證明過程如下。

證明:因為前提條件是next[j]=k,那么可以得知’p1.......pk-1‘=’p-k+1......pj-1‘,並且k滿足1<k<j和不存在h,1<k<h<j,滿足上面等式,即‘p1......ph-1’=‘pj-h+1......pj-1’,即此時的k值是最大的。

假設此時主串索引為i+1,模式串為j+1,那么‘p1......pj-1’=‘p2......pj’等式是肯定不能成立的。因為如果該等式成立,那么就會存在等式‘p1......pj-2’=‘p2......pj-1’成立,而根據next[j]=k的前提條件,我們已經得出不存在h,1<k<h,滿足等式‘p1......ph-1’=‘pj-h+1......pj-1’,而此時卻奇怪得出了存在j-1滿足上面等式,和已知條件產生矛盾。

所以,next[k]和j之間的所有字符都是不能滿足算法尋找的字符滿足的等式的條件的。因此,如果pk!=pj,那么接下來必須用從0到next[k]之間的字符和第j個字符比較匹配,所以本算法首先采用從第next[j]個字符和第j個字符比較匹配。

 

如果我證明的不對,還請大牛們把正確答案告訴我一下,讓我也得到正確的證明過程。謝謝!

 

終上所述,next的函數表達式如下所示:

                                 0,j=1;

                   next[j]=  MAX{k | ‘p1......pk-1’=‘pj-k+1......pj-1’,1<k<j},集合不為空;

                                1,其他情況。

            

                假設next[j]=k。

                                 next[j]+1=k+1,pj=pk;

                next[j+1]=1,pk!=pj,不存在字符u,1<u<k,滿足等式‘p1......pu’=‘pj-u+1......pj’;

                                 next[......next[next[k]]]+1,pk!=pj,找到了u,1<u<k,‘p1......pu’=‘pj-u+1......pj’。

本博客主要講的是如何按照地推的思想來計算出所有的next函數, 減少每次都掃描整個模式串計算next數值,通過遞歸算法可以利用前面已經計算出的next[j]的數值可以計算出next[j+1],這樣可以提高不少效率。 

 

KMP算法的求next數值函數代碼如下:其中綠色的行代表代碼,其它代表對每一行代碼的注釋。

void getNext(String p,int[] next)

{

     //next初始化next[1]=0,由於需要計算最大k值,因此從第2個字符開始查找匹配子串,使得u滿足1<u<j

     //’p1......pu-1‘=’pj-u+1......pj-1‘,本算法使用了兩個索引指針,i和j,並且初始化i=1,j=0。這里為了計算出next數        //值,主串和模式串都是使用的模式串數據。如

     //主串:    a b a a b c a c,i=1;

     //模式串:   a b a a b c a c,j=0.

      int i=1; next[1]=0; j=0;

   //線性掃描主串,主串不回朔,只能增加,而模式串可能會不斷的向右滑動,尋找字符u,使得pu=pj,求解                  //next[j+1]。

     while(i<=p.length)

      {

      //由於模式串不存在第0個,因此j==0代表主串和模式串肯定不匹配,此時,主串和模式串都必須增加1個索引,然       //后繼續匹配操作。此時j=0+1=1,next[i]=1和當主串和模式串的第0個字符比較得出的結果一致,即此時需要使用模式串的第1個字符和主串匹配比較。另外j==0條件還包含了一種情況是在模式串中找不到字符u,使得pu=pj,此時next[j]都等於1。

      //p[i]=p[j]條件表示主串和模式串匹配,因此,主串和模式串都需要增加一個字符索引,假設此時主串索引為i和模式串索引為j,可以得出,’p1......pj‘=’pi-j+1......pi‘,由於i和j都要增加一個字符索引,此時主串為i=i+1,模式串為j=j+1,所以next[i]=j;

           if(j==0||p[i]==p[j]) {++i;++j;next[i]=j;}

    //當j!=0並且p[i]!=p[j]時,執行下面else代碼,該代碼表示此時模式串需要向右移動,即為了使下一次while循環比較時候使用第next[j]個字符和主串中的第i個字符匹配比較。

           else j=next[j];

     }

  //當循環結束時候,next數組中存放的就是模式串的next初始化的全部數值。即當當前模式串中的字符和主串中的值不匹配時候,下一步需要使用哪一個字符和主串中的當前字符比較匹配。

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM