日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

学习KMP (概念 + 模板 + 例题: 子串查找)

發布時間:2023/12/3 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 学习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

字符串前綴后綴最大公共元素長度
A0
ABAB0
ABCA,ABC,BC0
ABCDA,AB,ABCD,CD,BCD0
ABCDAA,AB,ABC,ABCDA,DA,CDA,BCDA1
ABCDAB2
ABCDABD

字符ABCDABD
公共元素長度0000120

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 (概念 + 模板 + 例题: 子串查找)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。