(部分分析靈感及樣例來源於 @xehoth)
複習模板的時候打了幾遍,比當時學的時候感覺要清晰一些,於是抱着KMP在屏幕上滾來滾去滾來滾去滾來滾去~
吶,首先對於幾乎所有第一次接觸KMP的人來說的那個大坑(喂就是那個神奇的next[ ]數組),我們先按下不表。
假設模式串是 abcieruabc ,目標串是 abc
abcieruabc
---
abc
第一次匹配,匹配成功,如果不用KMP算法
abcieruabc
|
abc
第一個就不匹配,若是繼續用這種樸素的方法,下一次還是第一個就不匹配。(等等我們換一個更具特色的樣例)
ttittittyyios
-----|
ttitty
吶,如果用樸素的方法
ttittittyyios
-|
ttitty
再然後
ttittittyyios
|
ttitty
。。。。我們還是談談KMP好啦。
由於目標串中”tt”重複了兩次,則第二次匹配完全可以從第一次第二個”tt”所在的位置開始匹配。
當然,從某一個方面來說’t’也重複了幾次,但是
ttittittyyios
-|
ttitty
就匹配失敗了,所以要從最大重複的字串入手。
放道題來討論吧。
字符串匹配【KMP模板】
給定兩個由小寫字母構成的字符串 L 和 S 。
請你從左到右,找出子串 L 在母串 S 中每次出現的開始位置(匹配位置)。
輸入格式
第一行:給一個全由小寫字母構成的母串 S(0<S的長度≤1000000);
第二行:給一個全由小寫字母構成的子串 L(0<L的長度≤S的長度)。
輸出格式
按升序輸出一行一個整數,分別表示子串 L 在母串 S 中每次出現的開始位置。
如果子串 L 在母串 S 中沒有出現,則輸出“NO”。
樣例數據 1
輸入
yuabcierabcde
abc
輸出
3
9
樣例數據 2
輸入
abcdefg
abcdefu
輸出
NO
備註
【樣例1說明】
從第3個位置起,第一次匹配;
從第9個位置起,第二次匹配。
for(int i=2,j=0;i<=m;i++)
{
while(j!=0&&t[i]!=t[j+1])
j=next[j];
if(t[i]==t[j+1]);
j++;
next[i]=j;
}
這就是對目標串t的一個初始判定
其實後來二者匹配的時候代碼差不多,因爲第一個其實可以看做是目標串自己和自己匹配。只是第一次是不要求是否匹配完全,只要求找出是否存在可以匹配的來降低複雜度與時間;第二次就需要討論一下 j==m 的情況,及是否匹配完全,若完全,將標記 f 進行修改,輸出此時最後一個位置i減去目標串的長度m再加1,即此時目標串首位的位置。最終若f作了修改,,則繼續尋找下一個匹配點;若沒有,輸出”NO”,再找下一個匹配點。
bool f=1;
for(int i=1,j=0;i<=n;i++)
{
while(j!=0&&s[i]!=t[j+1])
j=next[j];
if(s[i]==t[j+1])
j++;
if(j==m)
{
cout<<i-m+1<<endl;
j=next[m],f=0;
}
}
if(f==1)
cout<<"NO"<<endl;
那麼大體上算法如何實現的已經說完了,接下來淺顯簡短地說一下那個神奇的next[ ]數組。(明明就是自己也不怎麼懂吧。。。。人艱不拆穿)
據說是
next 數組就是模式串前綴的最長公共前後綴的長度。
唔那大概是這樣的
ttitty 前綴 後綴 next
1 t - - 0
2 tt t t 1
3 tti t,tt ti,i 0
4 ttit t,tt,tti tit it t 1
5 ttitt …… …… ……
6 ttitty …… …… ……
(部分分析靈感及樣例來源於 @xehoth)
說了那麼多,還有完整版無水代碼。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int n,m;
int next[1000010];
char s[1000010],t[1000010];
int main()
{
scanf("%s%s",s+1,t+1);
n=strlen(s+1),m=strlen(t+1);
for(int i=2,j=0;i<=m;i++)
{
while(j!=0&&t[i]!=t[j+1])
j=next[j];
if(t[i]==t[j+1])
j++;
next[i]=j;
}
bool f=1;
for(int i=1,j=0;i<=n;i++)
{
while(j!=0&&s[i]!=t[j+1])
j=next[j];
if(s[i]==t[j+1])
j++;
if(j==m)
{
cout<<i-m+1<<endl;
j=next[m],f=0;
}
}
if(f==1)
cout<<"NO"<<endl;
return 0;
}
喵嗚~
(部分分析靈感及樣例來源於 @xehoth)
來自2017.10.10
——我認爲return 0,是一個時代的終結。