KMP算法

        1,## KMP算法的來源? ##

由於樸素匹配算法非常低效,需要挨個遍歷主串,所以有三位前輩,D.E.Knuth,J.H.Morris,和V.R.Pratt發表了一個模式匹配算法,可以大大避免重複遍歷的情況,我們稱這爲克努特–莫里斯–普拉特算法,簡稱KMP算法。

        2,## 何爲KMP算法? ##

KMP算法在匹配過程中發生失配時,並不是簡單的從原始串的下一個字符重新開始匹配,而是根據匹配過程中所得到的信息跳過不必要的匹配,從而達到高效的匹配算法。
下圖主串爲S串abcabcabcd,匹配串爲T串abcabcd

這裏寫圖片描述
當第一次匹配到S[6]!=T[6]時,主串不回溯,匹配串沒有回溯到回0位置,而是回溯到下標爲3的位置,繼續匹配,直到匹配完成。

這裏寫圖片描述

那爲何匹配串會回溯到下標爲3的位置呢?實際上KMP匹配算法實現了一個next數組,匹配串回溯到next數組對應下標所存儲的位置,此時next[6] = 3。

        3,## next數組的定義及計算 ##

(1),next數組的定義
next數組:存儲的是匹配串匹配失敗時回溯的位置
對於匹配串而言,滿足匹配串中存在兩個真子串相等,一個從0位置開始,一個以j-1位置結束,j爲當前訪問匹配串的下標,滿足的公式是p0…pk-1 == pj-k..pj-1;k爲next數組j下標所保存的值。

(2),next數組的計算
令next[0] = -1;next[1] = 0;next[j] = fun(next[j-1]);
根據p0…pk-1 = pj-k…pj-1公式可以求的,當next[j] = k;
next[j+1] 的值 就可以分爲兩種情況
if(pk == pj) next[j+1] = k+1; p0…pk = pj-k…pj
此時的pk就相當於sub[k],而pj也就相當於sub[j]

這裏寫圖片描述

if(pk != pj) k = next[k] ,然後在判斷pk與pj是否相等

這裏寫圖片描述

如果pk與pj一直不相等,直到k=-1,那就讓它執行next[j+1] = 0; 無相等的匹配串,直接回溯到匹配串的第一位

這裏寫圖片描述

p[j] != p[k] 直到 k = -1,不能在回退了。

這裏寫圖片描述

            4,## next數組代碼實現 ##              
思路 : 用next[j]求next[j+1],依此類推

void get_next(char *sub,int *next,int length)
{
    assert(sub != NULL && next != NULL);
    if(length < 1)
        return;

    next[0] = -1;
    next[1] = 0;
    int j = 1;  通過next[j] 計算 next[j+1]
    int k = 0;
    while(j < length - 1)
    {
            if(k == -1 || sub[k] == sub[j])  //如果相等,next數組++,如果k==-1,讓sub[j+1] = 0;回溯到0位置
            {
                next[++j] = ++k;
            }
            else
            {
                k = next[k];
            }
    }    
}
            5,## KMP算法代碼實現 ##
思路 : KMP匹配算法實現了一個next數組,
在匹配的時候主串不回溯,匹配串回溯到next數組
對應下標所存儲的位置,然後繼續進行匹配,
直到匹配完畢。

int KMP(const char *str, const char* sub)
{
    if(str == NULL || sub == NULL)
        return -1;
    int str_len = strlen(str);
    int sub_len = strlen(sub);

    if(sub_len > str_len || sub_len == 0)
        return -1;
    int next[255];
    get_next(sub,next,sub_len); //得到next數組

    int i = 0;
    int j = 0;
    while(i < str_len && j < sub_len)
    {
        //當j等於-1時,說明不匹配,主串後移,匹配串j++到0位置,繼續匹配,直到匹配完畢
        if(j == -1 || str[i] == sub[j]) //相等同時後移  
        {
            ++i;
            ++j
        }
        else
        {
            j = next[j]; //匹配串回溯到next數組所存儲的位置
        }
    }
    if(j >= sub_len)
        return i - j;
    return -1;
}

//測試一下 代碼
int main()
{
    char *str = "ababababc";
    char *sub = "ababc";
    int k = KMP(str,sub);
    printf("%d\n",k);
    return 0;
}

這裏寫圖片描述

結果顯示:從第四位開始匹配上了,例子很多,我這就只舉一個啦,接下來讓我看看KMP的時間複雜度。

        6,## KMP算法的時間複雜度 ##

對於主串的長度爲m,匹配串的長度爲n,進行匹配,KMP的時間複雜度爲O(m+n),相比於BF算法時間複雜度O(m*n)效率已經是提高的很多了。
對於KMP算法的優化,可以選擇去優化next數組來實現,有興趣的碼友可以自己實現一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章