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