那些有關求解next數組的算法

next數組的歷史算法

  有關字符串的模式匹配算法中,比較容易寫出的是樸素的匹配算法也就是一種暴力求解方式,可是因爲其時間複雜度爲子串長度和主串長度的乘積,例如strlen(subStr) = n,strlen(mainStr) = m,則其時間複雜度爲O(mn)。數組

  爲了可以獲得更有效的匹配算法,D.E.Knuth與V.R.Pratt和J.H.Morris同時發現,所以人們稱它爲克努特--莫里斯--普拉特操做(簡稱KMP算法)。KMP算法的關鍵是利用匹配失敗後的信息,儘可能減小模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個next()函數,函數自己包含了模式串的局部匹配信息。這也便是KMP算法的設計思想。函數

next數組的求解思路優化

  求next數組,next[j]表示,當模式串j位置與主串i位置處發生不匹配時,i指針不回溯,j指針回溯到next[j]的位置。spa

  對於求next[j]有三種狀況:設計

一、j = 0時,next[j] = -1;//即模式串的第一個字符與主串i位置發生不匹配,應將i跳過當前位置,從下一個位置和模式串的第一個字符繼續比較。指針

二、假設已知next[j] = k,即subStr[0,...,k-1] = subStr[j-k,j-1]。當subStr[k] = subStr[j]時,也就是說模式串知足subStr[0,...,k] = subStr[j-k,j],能夠得知next[j+1] = k + 1 = next[j] + 1;code

三、當subStr[k] != subStr[j]時,就須要從k位置以前去查找與subStr[j]匹配的位置,假設爲j'。這樣問題又能夠轉化爲第二種狀況,即next[j+1] = next[j'] + 1 = k' + 1。blog

三種求解next數組的算法字符串

  可是如何去求解next數組呢?有關這個問題,我思考了很長時間,下面給出幾種算法:

  算法一,嚴格根據next數組的定義:

 

 1 void getNext(char subStr[],int next[])
 2 {
 3     int i = 1, j = i - 1,k = -1;
 4     next[0] = -1;
 5     while (i < strlen(subStr))
 6     {
 7         //k = -1時表示j指針回溯到第一個字符的位置
 8         //subStr[k] == subStr[i-1]表示第k個字符和i - 1個字符相等,屬於狀況二
 9         if (k == -1 || subStr[k] == subStr[i-1])
10         {
11             next[i] = k + 1;
12             k = next[i];
13             i++;
14         }
15         //狀況三,不相等的話,要回溯j指針,subStr[j'] = subStr[i-1]的位置j'
16         else
17         {
18             int t = i - 2;
19             while (t>=0)
20             {
21                 if (subStr[t] == subStr[i - 1])
22                 {
23                     j = t;
24                     break;
25                 }
26                 t--;
27             }
28             if (t < 0)
29                 j = 0;
30             k = next[j];
31         }
32 
33     }
34 }

  算法二,算法的設計思想和算法一大體相同

void getNext(const char P[], int next[])
{
    int q, k;
    int m = strlen(P);
    next[0] = -1;//模版字符串的第一個字符的最大先後綴長度爲0
    for (q = 1; q < m; ++q)//for循環,從第二個字符開始,依次計算每個字符對應的next值
    {
        k = next[q - 1];
        while (k > 0 && P[q - 1] != P[k])//迭代地求出P[0]···P[q]的最大的相同的先後綴長度k
        {
            k--;
        }

        if (P[q-1] == P[k])//若是相等,那麼最大相同先後綴長度加1
        {
            k++;
        }
        if (k == -1)
            k = 0;
        next[q] = k;

    }
}

  算法三,更加優化的求解next數組的算法

void getNext(const char subStr[], int next[])
{
    int i = 1, j = -1;
    next[0] = -1;
    while (i < strlen(subStr))
    {
                //i從0開始的,屬於狀況二
                //j是先後綴長度
        if (j == -1 || subStr[i] == subStr[j])
        {
            i++;
            j++;
            next[i] = j;
        }
                //狀況三,不一樣則j指針回溯
        else
            j = next[j];
    }
}

  如今來進行總結一下,對於算法一和算法二來講,它們的時間複雜度是同樣的,可是相對於算法三來講,雖然不如算法三高效,可是比較容易理解!

PS:若是有誤的地方,請指出,共同進步!

相關文章
相關標籤/搜索