KMP-字串

安娜アンナ發表於2024-12-11

雖然我們可以根據板子題:兔子和兔子同樣實現線性字串匹配的速度。

但是也有更好的演算法KMP,其更高效快捷。

先看看什麼是KMP演算法?

例如 S = ababccabab 與T = abacabab

KMP-字串

我們來看next陣列 ,依次S[i]與T[i]進行比較,若S[i]!= T[i]

我們只需要讀取next[i]陣列下的內容;例如第一次 各個前5個字元 T= aba|c|a 與S = aba|b|c中 abaca與ababc的aba匹配 但是c與b不匹配了

我們可以讀取next[4] == 1, 即c下的陣列;

往前匹配T[next[4]] (即T[1])是否等於 S[4] ; 很好,剛好相等;

我們繼續比較接下來從T[2]開始與S[5]開始比較即可,前兩位可以不再進行匹配;

如何建立NEXT陣列:

如下所示:

KMP-字串

其中(1) 為 S[I] == S[J]的情況

其中(2)就是回溯,其實本質就是,因為S[I] == S[J] 當前情況下b!=c,所以我們開始回溯,往回跑。即判斷s[next[i-1]]是否等於s[i],若不是,繼續回溯。

其實可以理解,當前的字首是大括號,那麼同樣存在字首中有字首和字尾的關係,這樣一直回溯。知道找到s[j = next[j-1]]==s[i]的情況 。

其中(3)字首串--相對(2)操作的基礎情況下的

附上程式碼

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

const int N = 1e7;
int next_[N];

void get_next(string T) {
    int j = 0;
    next_[0] = 0;
    for (int i = 1; i < T.size(); i++) {
        while (j > 0 && T[i] != T[j]) {
            j = next_[j - 1]; // 回溯,尋找上一個的下一個點是否為T[i]
        }
        if (T[i] == T[j]) {
            j++;
        }
        next_[i] = j;
    }
    for (int i = 0; i < T.size(); i++) {
        cout << next_[i] << " ";
    }
    cout << endl;
}

int KMP(string S, string T) {
    int m = S.size();
    int n = T.size();
    int j = 0;
    for (int i = 0; i < m; i++) {
        while (j > 0 && S[i] != T[j]) {
            j = next_[j - 1]; // 回溯
        }
        if (S[i] == T[j]) {
            j++;
        }
        if (j == n) {
            int ans = i - n + 1;
            return ans;
        }
    }
}

int main() {
    string text = "abacabacacabcababccabab";
    string pattern = "ababccabab";

    // 列印文字和模式串
    cout << "Text: ";
    for (char c : text) {
        cout << c << " ";
    }
    cout << endl;

    cout << "Pattern: ";
    for (char c : pattern) {
        cout << c << " ";
    }
    cout << endl;

    // 計算並列印部分匹配表
    cout << "Next array: ";
    get_next(pattern);

    // 執行KMP演算法進行匹配
    cout<<"res="<<KMP(text, pattern);

    return 0;
}
    

相關文章