KMP演算法-之next數組-詳解,kmp演算法next數組

來源:互聯網
上載者:User

KMP演算法-之next數組-詳解,kmp演算法next數組

我們在一個母字串中尋找一個子字串有很多方法。KMP是一種最常見的改進演算法,它可以在匹配過程中失配的情況下,有效地多往後面跳幾個字元,加快匹配速度。

當然我們可以看到這個演算法針對的是子串有對稱屬性,如果有對稱屬性,那麼就需要向前尋找是否有可以再次匹配的內容。

 

在KMP演算法中有個數組,叫做首碼數組,也有的叫next數組,每一個子串有一個固定的next數組,它記錄著字串匹配過程中失配情況下可以向前多跳幾個字元,當然它描述的也是子串的對稱程度,程度越高,值越大,當然之前可能出現再匹配的機會就更大。

這個next數組的求法是KMP演算法的關鍵,但不是很好理解,我在這裡用通俗的話解釋一下,看到別的地方到處是數學公式推導,看得都蛋疼,這個篇文章僅貢獻給不喜歡看數學公式又想理解KMP演算法的同學。

 

1、用一個例子來解釋,下面是一個子串的next數組的值,可以看到這個子串的對稱程度很高,所以next值都比較大。

位置i

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

首碼next[i]

0

0

0

0

1

2

3

1

2

3

4

5

6

7

4

0

子串

a

g

c

t

a

g

c

a

g

c

t

a

g

c

t

g

申明一下:下面說的對稱不是中心對稱,而是中心字元塊對稱,比如不是abccba,而是abcabc這種對稱。

(1)逐個尋找對稱串。

這個很簡單,我們只要迴圈遍曆這個子串,分別看前1個字元,前2個字元,3個... i個 最後到15個。

第1個a無對稱,所以對稱程度0

前兩個ag無對稱,所以也是0

依次類推前面0-4都一樣是0

前5個agcta,可以看到這個串有一個a相等,所以對稱程度為1前6個agctag,看得到ag和ag對成,對稱程度為2

 下面是如何進行對這個next值求解的過程:

例如一個字串:bread

首先,要瞭解兩個概念:"首碼"和"尾碼"。 "首碼"指除了最後一個字元以外,一個字串的全部頭部組合;"尾碼"指除了第一個字元以外,一個字串的全部尾部組合。

  

  "next值"就是"首碼"和"尾碼"的最長的共有元素的長度。以"ABCDABD"為例,

  - "A"的首碼和尾碼都為空白集,共有元素的長度為0;

  - "AB"的首碼為[A],尾碼為[B],共有元素的長度為0;

  - "ABC"的首碼為[A, AB],尾碼為[BC, C],共有元素的長度0;

  - "ABCD"的首碼為[A, AB, ABC],尾碼為[BCD, CD, D],共有元素的長度為0;

  - "ABCDA"的首碼為[A, AB, ABC, ABCD],尾碼為[BCDA, CDA, DA, A],共有元素為"A",長度為1;

  - "ABCDAB"的首碼為[A, AB, ABC, ABCD, ABCDA],尾碼為[BCDAB, CDAB, DAB, AB, B],共有元素為"AB",長度為2;

  - "ABCDABD"的首碼為[A, AB, ABC, ABCD, ABCDA, ABCDAB],尾碼為[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的長度為0。


這裡要注意了,想是這樣想,編程怎麼實現呢?

只要按照下面的規則:

a、當前面字元的前一個字元的對稱程度為0的時候,只要將當前字元與子串第一個字元進行比較。這個很好理解啊,前面都是0,說明都不對稱了,如果多加了一個字元,要對稱的話最多是當前的和第一個對稱。比如agcta這個裡面t的是0,那麼後面的a的對稱程度只需要看它是不是等於第一個字元a了。

 

b、按照這個推理,我們就可以總結一個規律,不僅前面是0呀,如果前面一個字元的next值是1,那麼我們就把當前字元與子串第二個字元進行比較,因為前面的是1,說明前面的字元已經和第一個相等了,如果這個又與第二個相等了,說明對稱程度就是2了。有兩個字元對稱了。比如上面agctag,倒數第二個a的next是1,說明它和第一個a對稱了,接著我們就把最後一個g與第二個g比較,又相等,自然對稱成都就累加了,就是2了。

 

c、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到這裡應該一點難度都沒有吧,如果你覺得有難度說明我寫的太失敗了。

當然不可能會那麼順利讓我們一直對稱下去,如果遇到下一個不相等了,那麼說明不能繼承前面的對稱性了,這種情況只能說明沒有那麼多對稱了,但是不能說明一點對稱性都沒有,所以遇到這種情況就要重新來考慮,這個也是痛點所在。

 

(2)回頭來找對稱性

這裡已經不能繼承前面了,但是還是找對稱成都嘛,最愚蠢的做法大不了寫一個子函數,尋找這個字串的最大對稱程度,怎麼寫方法很多吧,比如尋找出所有的當前字串,然後向前走,看是否一直相等,最後走到子串開頭,當然這個是最蠢的,我們一般看到的KMP都是最佳化過的,因為這個串是有規律的。

在這裡依然用上面表中一段來舉個例子:   

位置i=0到14如下,我加的括弧只是用來說明問題:

(a g c t a g c )( a g c t a g c) t

我們可以看到這段,最後這個t之前的對稱程度分別是:1,2,3,4,5,6,7,倒數第二個c往前看有7個字元對稱,所以對稱為7。但是到最後這個t就沒有繼承前面的對稱程度next值,所以這個t的對稱性就要重新來求。

這裡首要要申明幾個事實

1、t 如果要存在對稱性,那麼對稱程度肯定比前面這個c 的對稱程度小,所以要找個更小的對稱,這個不用解釋了吧,如果大那麼t就繼承前面的對稱性了。

2、要找更小的對稱,必然在對稱內部還存在子對稱,而且這個t必須緊接著在子對稱之後。

如說明。

 

從上面的理論我們就能得到下面的首碼next數組的求解演算法。

void SetPrefix(const char *Pattern, int prefix[])

{

     int len=CharLen(Pattern);//模式字串長度。

     prefix[0]=0;

     for(int i=1; i<len; i++)

     {

         int k=prefix[i-1];

         //不斷遞迴判斷是否存在子對稱,k=0說明不再有子對稱,Pattern[i] != Pattern[k]說明雖然對稱,但是對稱後面的值和當前的字元值不相等,所以繼續遞推

         while( Pattern[i] != Pattern[k]  &&  k!=0 )               

             k=prefix[k-1];     //繼續遞迴

         if( Pattern[i] == Pattern[k])//找到了這個子對稱,或者是直接繼承了前面的對稱性,這兩種都在前面的基礎上++

              prefix[i]=k+1;

         else

              prefix[i]=0;       //如果遍曆了所有子對稱都無效,說明這個新字元不具有對稱性,清0

     }

}

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.