KMP算法-NFA版

首先是KMP算法的標準模板

#include <iostream>
#include <cstring>
using namespace std;

int* buildNext(char* P){
    int m = strlen(P);
    int j = 0;
    int *N = new int[m]; // N[j]表示對於模式串的長度爲j的前綴,能錯位匹配的最大長度
                         // N[j]也可以解釋爲:如果第j個字符失配了,那麼應該從N[j]開始繼續嘗試
    int t = N[0] = -1; // t是當前能錯位匹配的最大長度
    while(j < m-1){
        if(t < 0 || P[j] == P[t]) // 如果下一位也能匹配
            N[++j] = ++t; // 記錄,j移向下一位,t增大1
        else // 下一位不能匹配
            t = N[t]; // 窗口向右滑動
    }
    return N;
}

int kmp(char* T, char* P){
    int n = strlen(T);
    int m = strlen(P);
    int* N = buildNext(P);
    int i = 0, j = 0;
    while(i < n && j < m){
        if(j < 0 || T[i] == P[j])
            ++i, ++j;
        else
            j = N[j];
    }
}

int main()
{
    char s1[1000005], s2[1000005];
    cin >> s1 >> s2;
    cout << kmp(s1, s2) << endl;
    system("pause");
    return 0;
}

經過改進的KMP算法如下,改動在第9行。

int* buildNext(char* P){
    int m = strlen(P);
    int j = 0;
    int *N = new int[m]; // N[j]表示對於模式串的長度爲j的前綴,能錯位匹配的最大長度
                         // N[j]也可以解釋爲:如果第j個字符失配了,那麼應該從N[j]開始繼續嘗試
    int t = N[0] = -1; // t是當前能錯位匹配的最大長度
    while(j < m-1){
        if(t < 0 || P[j] == P[t]) // 如果下一位也能匹配
            ++j, ++t, N[j] = (P[j] != P[t] ? t : N[t]); // [little change here]
            // 這樣理解:N[j]也可以解釋爲:如果第j個字符失配了,那麼應該從N[j]開始繼續嘗試
            // 如果P[N[j]]與P[j]相同,一定會繼續失敗,與其等到時候再滑動窗口,不如現在就滑動,所以修改爲N[j] = N[N[j]]
            // 根據遞歸的方法,如果0~j-1的N[]都弄成了最優的,那麼當前也會是最優的
        else // 下一位不能匹配
            t = N[t]; // 窗口向右滑動
    }
    return N;
}

最後來一道KMP模板題。與上述代碼不同的地方在於,本題要求輸出所有的匹配位置,因此當j == m時不退出循環,而是繼續滑動窗口(j = N[j]),當然,next數組的長度也要相應地從m變爲m+1

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int* buildNext(char* P){
    int m = strlen(P);
    int* N = new int[m+1];
    int j = 0;
    int t = N[0] = -1;
    while(j < m){
        if(t < 0 || P[t] == P[j])
            N[++j] = ++t;
        else
            t = N[t];
    }
    return N;
}

int main()
{
    char T[1000005], P[1000005];
    scanf("%s%s", T, P);
    int n = strlen(T);
    int m = strlen(P);
    int i = 0, j = 0;
    int* N = buildNext(P);
    while(i < n){
        if(j < 0 || T[i] == P[j])
            ++i, ++j;
        else
            j = N[j];
        if(j == m){
            printf("%d\n", i-j+1);
            j = N[j]; // 常規滑動窗口
        }
    }
    for(j = 1; j <= m; ++j)
        printf("%d ", N[j]);
    printf("\n");
    system("pause");
    return 0;
}

由於這道題的特殊要求,改進後的next數組無法滿足,所以不採取優化版本

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