為什麼我們要學習KMP呢?這就不得不說起當年暑假在校隊集訓的時候,苦逼做不出題目的痛苦時光了。
三個人看著題目中字串匹配的那個環節,思索了整整三個小時。
不得不說,從0到1,遠比在前人的肩膀上前行要難得多。真不知的這些變態大佬是怎麼想出來的。
先來提及一下,當時我們用人腦想出來的程式碼(我沒有說他們不是人:對於大部分人來說,那肯定就是一個個照著去比對就行了,然而很遺憾的是:
可惜時間太短,我無法愛上你——超時......
這就是那所謂的BP演算法
1.樸素模式匹配(BP)
由於這個真的太基礎,懶得廢話了。就是應該去主串和模式串一個個去配對,如果該字元匹配,則j++,否則j=0,然後i++
int index_BP(string s,string p){
int sl=s.length(),pl=p.length();
for(int i=0;i<=sl-pl;i++){
int flag=1;
for(int j=0;j<pl;j++){
if(s[i+j]=!p[j]){
flag=0;
break;
}
}
if(flag)
return i;
}
return -1;
}
2.KMP演算法(三個人名...)
(1)字首與字尾
什麼是字首和字尾捏?
ju個栗子,比如說有個字串“abac”,那麼對於該字串而言,“a”,“ab”,“aba”則是它的字首(PS:嚴格來講,“abac”和空串也可以算作字首)。
相應的,對於字尾而言就是,“c”,“ac”,“bac”等。
(2)next陣列
這個時候,可能就會想前字尾和字串的匹配有什麼關係呢?其實,重點就在於資訊的重複利用。
比如說,對於模式串“abdad”而言,如果在第5個字元“d”的時候不匹配,那麼我們不需要再次去重頭可以匹配,因為對於字串“abda”而言,有相同的前字尾“a”。
所以我們只需要從“b”開始進行相應的配對即可。
從這裡就可以看出,KMP演算法實質就是利用當前字元前的字元子串的相同前字尾,來跳過大量沒有意義的重複匹配。
這個是相關的動畫。。。有點醜,而且有點慢,但是,菜狗的我不會調速度
接下來就是求next陣列了,next陣列是利用模式串得到的。
那為什麼要叫做next陣列呢?那是因為如果當前字元不匹配,下一個要比對的模式串的字元是哪個。
相應的計算公式如上所示。
(3)相關的程式碼
int mynext[200000];
void get_next(string s){
int l=s.size(),k=-1;
mynext[0]=-1;
for(int i=1;i<=l;){
if(k==-1||s[i]==s[k]){
i++;k++;
mynext[i]=k;
}
else k=mynext[k];
}
}
int index_kmp(string s,string p,int pos){
int i=pos,j=0;
int sl=s.size(),pl=p.size();
for(i=pos,j=0;i<sl&&j<pl;){
if(j==-1||s[i]==p[j]){
i++;j++;
}
else j=mynext[j];
}
if(j>=pl) return i-pl;
else return -1;
}
3.相關例題
P3375 【模板】KMP - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
Password - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)