自己對KMP算法的理解,主要是改變了對next求法的定義


 最近在學算法,在看KMP算法時總感覺不怎么明白,網上也看了那么多關於KMP算法的介紹,還是沒怎么看懂,最后索性按照自己的理解,然后寫出代碼,主要是改變了next求解的定義。看了其他大牛在說KMP算法前都先說下BF算法,我也跟隨前輩,先說下BF算法。

一.BF算法

    BF算法是普通的模式匹配算法,BF算法的思想就是將目標串S的第一個字符與模式串P的第一個字符進行匹配,若相等,則繼續比較S的第二個字符和P的第二個字符;若不相等,則比較S的第二個字符和P的第一個字符,依次比較下去,直到得出最后的匹配結果。

    舉例說明:

    S:  ababcababa

    P:  ababa

  BF算法匹配的步驟如下

           i=0                                   i=1                             i=2                         i=3                          i=4

  第一趟:ababcababa         第二趟:ababcababa      第三趟:ababcababa    第四趟:ababcababa    第五趟:ababcababa

             ababa                            ababa                          ababa                        ababa                       ababa

            j=0                                   j=1                            j=2                         j=3                         j=4(i和j回溯)

 

              i=1                                 i=2                           i=3                            i=4                        i=3

 第六趟:ababcababa         第七趟:ababcababa       第八趟:ababcababa     第九趟:ababcababa   第十趟:ababcababa

              ababa                              ababa                           ababa                        ababa                        ababa

             j=0                                  j=0                           j=1                           j=2(i和j回溯)            j=0

 

              i=4                                    i=5                          i=6                           i=7                          i=8

第十一趟:ababcababa       第十二趟:ababcababa    第十三趟:ababcababa   第十四趟:ababcababa   第十五趟:ababcababa

                     ababa                               ababa                           ababa                          ababa                          ababa

               j=0                                    j=0                         j=1                            j=2                         j=3

 

                    i=9

第十六趟:ababcababa

                       ababa

                    j=4(匹配成功)

代碼實現:

int BFMatch(char *s,char *p)
{
int i,j;
i=0;
while(i<strlen(s))
{
j=0;
while(s[i]==p[j]&&j<strlen(p))
{
i++;
j++;
}
if(j==strlen(p))
return i-strlen(p);
i=i-j+1; //指針i回溯
}
return -1;
}


其實在上面的匹配過程中,有很多比較是多余的。在第五趟匹配失敗的時候,在第六趟,i可以保持不變,j值為2。因為在前面匹配的過程中,對於串S,已知s0s1s2s3=p0p1p2p3,又因為p0!=p1!,所以第六趟的匹配是多余的。又由於p0==p2,p1==p3,所以第七趟和第八趟的匹配也是多余的。在KMP算法中就省略了這些多余的匹配。(出處:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html

二.KMP算法

    KMP算法之所以叫做KMP算法是因為這個算法是由三個人共同提出來的,就取三個人名字的首字母作為該算法的名字。其實KMP算法與BF算法的區別就在於KMP算法巧妙的消除了指針i的回溯問題,只需確定下次匹配j的位置即可,使得問題的復雜度由O(mn)下降到O(m+n)。

  在KMP算法中,為了確定在匹配不成功時,下次匹配時j的位置,引入了next[]數組,next[j]的值表示P[0...j-1]中最長后綴的長度等於相同字符序列的前綴。

  我對對於next[]數組的定義如下:

 1) next[j] = -1  j = 0

 2) next[j] = max(k): 0<=k<j   P[0...k-1]=P[j-k,j-1]

相信很多算法中都是這樣定義的

 1) next[j] = -1  j = 0

 2) next[j] = max(k): 0<=k<j   P[0...k-1]=P[j-k,j-1]

 3) next[j] = 0  其他

我覺得3和2其實可以合並的這樣,如果前綴和后綴的相似元素為0個我們就說next[j]=0,這樣在程序設計上更簡單些

 如:

 P      a    b   a    b   a

 j      0    1   2    3   4

 next    -1   0   0    1   2

 即next[j]=k>0時,表示P[0...k-1]=P[j-k,j-1]

 因此KMP算法的思想就是:在匹配過程稱,若發生不匹配的情況,如果next[j]>=0,則目標串的指針i不變,將模式串的指針j移動到next[j]的位置繼續進行匹配;若next[j]=-1,則將i右移1位,並將j置0,繼續進行比較。

代碼實現如下:

int KMP(char *Text,char * Pattern)
{
if(!Text || !Pattern || Pattern[0] == '/0' || Text[0] == '/0')
return -1;
int len = 0;
const char *c = Pattern;
while(*c++ != 0)
{
++len;
}

int * next = new int[len+1];
get_next(Pattern,next);

int index = 0,i=0,j=0;
while(Text[i] != 0 && Pattern[j] != 0)
{
if (Text[i] == Pattern[j])
{
++i;
++j;
}
else
{
index += j-next[j];
if(next[j] != -1)
j = next[j]; //若當前匹配不成功則找到下次開始匹配的位置
else
{
j=0; //如果一開始就匹配不成功則向后移
++i;
}
}
}

delete []next;
if(Pattern[j] == 0)
return index;
else
return -1;


}


因此KMP算法的關鍵在於求算next[]數組的值,即求算模式串每個位置處的最長后綴與前綴相同的長度。

   根據定義next[0]=-1,

   若有next[j]=k;即表示為:

   在0<=k<j-1中,當m=0:1:k俊滿足P[j-1-m] = P[m],即k為重復元素最多的個數

   因此可以這樣去實現:

void get_next(char * P,int * next)
{
int k,j;
j = 1;
next[0] = -1; //第一個next的值始終為-1;其實也可以寫做其他標記,僅僅是表現起始位置而已

while(P[j] != 0)
{
k = 0;
while(k<j-1)
{
if(P[j-1-k] == P[k])
{
++k; //在P[0,...,j-1]中找相似的部分的個數,如ababc中c之前相似的為2個,這也是KMP在下一次移動時移動的位數
}
else
break; //找出其中最大的相似
}
next[j] = k;
j++; //j后移,每一個位置的都找出來
}
}


上述是我對KMP算法的理解,主要是改變了對next求解的定義,以及代碼的實現。本人最近在看KMP算法看到很多大牛關於KMP算法的解釋,文中借鑒了很多,但在學習的過程中感覺next數組晦澀難懂,代碼也不易看懂,所以就自己定義了next的求解,由於是菜鳥所以不知道這樣的next的定義有漏洞沒,歡迎指正。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com