雖然我們可以根據板子題:兔子和兔子同樣實現線性字串匹配的速度。
但是也有更好的演算法KMP,其更高效快捷。
先看看什麼是KMP演算法?
例如 S = ababccabab 與T = abacabab
我們來看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陣列:
如下所示:
其中(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;
}