《原神攻略》2.0版本霧切之回光武器分析
前言:
到底什麼是kmp呢?本人認為kmp的優勢在於根據之前的資訊來快速更新現在的資訊,從而提高時間的效率(類似於記憶化搜尋?)在此預設大家都會kmp。
主要用處:
一.字串匹配
程式碼:
#include<bits/stdc++.h> #define maxn 1000001 using namespace std; int kmp[maxn]; char a[maxn],b[maxn]; int la,lb; int j; int main() { cin>>a+1>>b+1; la=strlen(a+1); lb=strlen(b+1); j=0; kmp[1]=0; for(int i=2;i<=lb;i++) { while(j && b[j+1]!=b[i]) j=kmp[j]; if(b[j+1]==b[i]) j++; kmp[i]=j; } j=0; for(int i=1;i<=la;i++) { while(j && b[j+1]!=a[i]) j=kmp[j]; if(b[j+1]==a[i]) j++; if(j==lb) { cout<<i-lb+1<<endl; j=kmp[j]; } } for(int i=1;i<=lb;i++) cout<<kmp[i]<<' '; return 0; }
重點:kmp[i]陣列求的即為i位置匹配失敗時往前跳的花費最小位置,即模式串i位置前與第一位後的最長公共子串。
例:abaabaa
如果此時匹配失敗位置6,則此時直接將指標指向3號,因為1-2號=4-5號,所以既然已經匹配到了第6號說明此時匹配串匹配失敗的位置前2個=模式串4-5=模式串1-2號,所以下次重新匹配直接從3號開始,不必再從1號開始,大大節約了時間,此為此演算法之精髓。
時間複雜度:O(N)
二.4391(luogu)最小迴圈子串
給你一個字串s1,它是由某個字串s2不斷自我連線形成的。但是字串s2是不確定的,現在只想知道它的最短長度是多少。
Eg:cabcabca
Ans:cab
題解:答案即為l-kmp[l=s.size()];
證明:
(圖在原稿中,無法上傳,此處略去,如果需要可私信作者)
綠色為ans,將字串等量分段,由kmp陣列的特性得知則黃色1=綠色2=黃色2=紅色1=紅色2=藍色1=藍色2=草綠色1(數字為排數),則此時只用證明綠色前一半部分包含灰色,灰色2=草綠色前一部分=綠色前一部分,則得證。
三:2375(luogu)
給一個字串,求每一個位置的公共前後綴且滿足前後綴不交叉的數量。
題解:
先求出num1陣列表示每個位置交叉的前後綴數量,num1即為一直跳nxt直至nxt=0時的次數(注意前後綴既不能少前面也不能少後面,和迴文串不要搞混了,一個公共前後綴不包含別的前後綴,具有唯一性),即可在求
這個題對kmp陣列的意義考察的較好,做完這個題,應該會對kmp有更高的認識。
總結
kmp演算法主要用於字串單匹配,前後綴處理一些場景中,時間複雜度也相對可觀,程式碼量較小,但遇到多匹配時,只能上AC自動機了,這可能有樹狀陣列和線段樹的意味?