KMP演算法簡析丨板子題
阿新 • • 發佈:2022-03-31
暫且就先以一道洛谷板子題作為講解吧,
直接上程式碼,部分程式碼參考了優秀的大佬們的優秀的題解。(確實人家的碼風很好嗚嗚嗚)
1 #include<bits/stdc++.h> 2 #define ios ios::sync_with_stdio(false);cin.tie(0),cout.tie(0) 3 typedef long long ll; 4 //const ll mod = 1e9+7; 5 //const ll mod = 1000003; 6 #define endl "\n" 7 using namespace std; 8 9 const ll LEN = 1000001; 10int KMP[LEN]; //用於記錄 當匹配到模式串的第i位之後失配 跳轉到模式串的位置的陣列 也可以理解為當前位置j前面的最長前後綴的長度 11 int len_s; //str主串的長度 12 int len_p; //pattern_str模式串的長度 13 int j; //用於模式串的指標,可以看作表示當前已經匹配完的模式串的最後一位的位置 14 //說人話就是 j表示模式串匹配到哪裡了 15 char s[LEN],p[LEN]; //主串和模式串的宣告 感覺string也可以 但是如果想從下標為1開始操作就不太方便了 而且string目測會慢一些16 int main(){ 17 cin >> s + 1; //這裡+1的原因是讓操作的下標從1開始 18 cin >> p + 1; 19 len_s = strlen(s + 1); 20 len_p = strlen(p + 1); 21 j = 0; 22 for (int i = 2; i <= len_p; i++){ //讓模式串自己匹配自己獲取KMP陣列 23 while ( j && p[i] != p[j + 1]){ 24 j = KMP[j];25 } 26 if(p[j + 1] == p[i]){ 27 j++; 28 } 29 KMP[i] = j; 30 } 31 j = 0; 32 for(int i = 1; i <= len_s; i++){ //與主串相比,從而輸出匹配位置 33 while(j > 0 && p[j+1] != s[i]){ 34 j = KMP[j]; 35 } 36 if (p[j + 1] == s[i]) { 37 j++; 38 } 39 if (j == len_p) { 40 cout << i - len_p + 1 << endl; 41 j = KMP[j]; 42 } 43 } 44 for (int i = 1; i <= len_p; i++){ 45 cout << KMP[i] << " "; 46 } 47 return 0; 48 }
這裡特別說明一下,將j指標回溯,其實等價於將模式串按照匹配的最長前後綴移動,這兩個的效果是一樣的。
實在不理解就畫個圖理解一下就理解了(什麼奇怪的句子)。
圖解:
初態:
第一次不匹配:
目前狀態的最長前後綴:j = 2
接下來開始回溯,注意,左邊是回溯,右邊是移動模式串,兩種方式等價:
現在,j移動到了2的位置。
於是我們就可以迴圈這個操作,直到得出結果。