学习KMP (概念 + 模板 + 例题: 子串查找)
我又回來了,感jio這幾天有點勤啊!!
這一次我?guī)е鳮MP來了,
文章目錄
- KMP介紹
- 模板
- 例題: 子串查找
- 題目
- 暴力題解
- KMP題解
- 代碼實現(xiàn)
KMP介紹
KMP,即 Knuth-Morris-Pratt 字符串查找算法,由Donald Knuth、Vaughan Pratt、James H. Morris三人于1977年聯(lián)合發(fā)表,故取這3人的姓氏命名此算法。
KMP的算法流程如下:
假設(shè)字符串S現(xiàn)匹配到位置i,模式串P匹配到位置j:
①若j==-1,或S[i]==P[j],則i++,j++,繼續(xù)后面的匹配
②若j!=-1且S[i]!=P[j],則i不變,j=next[j]
(這樣就意味著當每次失配時,模式串P相對于字符串S向右移動了j-next[j]位)
這樣就不用每次從頭開始跑了
那么我們來看看next數(shù)組是怎么計算的。
①尋找模式串P的最長前綴、后綴
假設(shè)字符串S為ABCDAB ABCDABCDABDE,模式串P為ABCDABD
| A | 無 | 無 | 0 |
| AB | A | B | 0 |
| ABC | A,AB | C,BC | 0 |
| ABCD | A,AB,ABC | D,CD,BCD | 0 |
| ABCDA | A,AB,ABC,ABCD | A,DA,CDA,BCDA | 1 |
| ABCDAB | … | … | 2 |
| ABCDABD | … | … |
②
| 公共元素長度 | 0 | 0 | 0 | 0 | 1 | 2 | 0 |
ABCDAB ABCDABCDABDE
ABCDABD
這時D和空格匹配,
那么P向右移動的位數(shù)=已匹配的字符數(shù)(6)-上一個字符的最大公共元素長度(B的時候長度為2)=4
當?shù)谝晃灰膊黄ヅ涞臅r候,直接向后移動一位。
模板
void kmp () {int len = strlen ( p );for ( int i = 1, j = 0;p[i];i ++ ) {while ( j && p[j] != p[i] )j = nxt[j];nxt[i + 1] = p[j] == p[i] ? ++ j : 0;}int tot = 0;for ( int i = 0, j = 0;s[i];i ++ ) {while ( j && p[j] != s[i] )j = nxt[j];if ( p[j] == s[i] && ++ j == len ) {tot ++;j = nxt[j];}}printf ( "%d", tot ); }看懂了的話,就來到例題試試吧!
例題: 子串查找
題目
給出兩個字符串s和p,其中p為s的子串,求p在s中的出現(xiàn)次數(shù)。
必須完全連續(xù)匹配,且s,p全部為大寫或小寫字母
輸入格式
第一行為一個字符串,即為s
第二行為一個字符串,即為p
輸出格式
輸出一個整數(shù),表示p在s中的出現(xiàn)次數(shù)。
輸入輸出樣例
輸入
zyzyzyz
zyz
輸出
2
N<=1000000
暴力題解
暴力很好想,就是一個一個地跑,一旦遇到不匹配的,就從頭開始重新跑:
假設(shè)字符串S現(xiàn)匹配到位置i,模式串P匹配到位置j。
那么,當S[i]==P[j]的時候,i++,j++,并繼續(xù)下一次匹配。
若S[i]!=P[j],則i=i-j+1,j=0,即將i移動到此輪匹配的下一位,j置0重新匹配。
代碼就不獻給諸君了,因為我懶得寫
KMP題解
遇到某些題目,例如給多個匹配串和一個模式串,大家千萬不要跑n遍next數(shù)組,
沒有必要因為,next根本沒有變。
因為這道題是個模板,看懂了KMP介紹的各位小可愛,就不需要再解釋為什么了?
我重點分析一下代碼這玩意兒:
nxt[i]的含義:在模式串p中,從0到i-1為止前綴后綴字符最大匹配長度。
第一個for循環(huán)就是處理出nxt,因為當我們處理到i時,要找到0~i-1的nxt,
怕麻煩,我們可以把nxt整體往后移1,這樣i就對應(yīng)了nxt[i-1]
注意循環(huán)里的while (j=nxt[j])不能寫成j=0,舉個栗子:
abcdefababacdefababxyz
abacdefabab
ans:2,你如果寫成0,wrong ans:1
因為寫成零的話當你走到模式串p的倒數(shù)第二個字符,即a的時候你沒有存下1,
導(dǎo)致最后循環(huán)到b的時候nxt沒有成為2,反而是0,在s中查找的時候就會錯過一個答案開頭(關(guān)注加粗)
第二個循環(huán)就是找個數(shù)了,前文提到是將整個p右移x-nxt[i],而我則是寫成將p的下標變成nxt[i]
控制s不變,這樣就是個相對對應(yīng)關(guān)系了,我們反正是用下標操作,不一定要移動兩個字符串
好好理解吧~~
while里的j也不能直接賦值成為0,不然你連樣例都過不了,還做什么??
具體原因與上面如出一轍,我不再闡釋。。。
一句話就是如果寫成j=0,那么那些所有模式串結(jié)尾等于模式串開頭的數(shù)據(jù),基本上你都涼了~~
abcdabcda
abcda
這種類似數(shù)據(jù)你可能都要少算,因為s有兩個模式串共用了一個字符,你就GG了
我知道很多小可愛,看完后。。
也就只有大牛 不有可能,大佬都被我搞蒙了,看來我技術(shù)還是不錯的~
代碼實現(xiàn)
#include <cstdio> #include <cstring> #define MAXN 1000005 char s[MAXN], p[MAXN]; int nxt[MAXN];void kmp () {int len = strlen ( p );for ( int i = 1, j = 0;p[i];i ++ ) {while ( j && p[j] != p[i] )j = nxt[j];nxt[i + 1] = p[j] == p[i] ? ++ j : 0;}int tot = 0;for ( int i = 0, j = 0;s[i];i ++ ) {while ( j && p[j] != s[i] )j = nxt[j];if ( p[j] == s[i] && ++ j == len ) {tot ++;j = nxt[j];}}printf ( "%d", tot ); }int main() {scanf ( "%s %s", s, p );kmp ();return 0; }那個男人,帶著KMP走來了,又一臉懵逼地離開了?
總結(jié)
以上是生活随笔為你收集整理的学习KMP (概念 + 模板 + 例题: 子串查找)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么屏蔽监控摄像头怎样脱离小米路由器的监
- 下一篇: 数数字