本博客中令人費解的‘/’是用來/分割/信息區間/幫助理解/的
KMP就是暴力算法少跳幾步
我們在這裏又可以體會到用空間(複雜度)換時間(複雜度)的思想
- next數組:
其他人寫的什麼最長前綴後綴+1是next的本質
用途上next就是/現在搜到這位/失配/跳回的位置 - get_next函數:
本質上就是模式串自己跟自己匹配一遍
BF算法,記錄同前/後綴的位置
get_next
char s[NNN],t[NNN];//區間均爲[1,len]
int next[NNN],lens,lent;
inline void get_next(){
int i=1,j=0;
while(i<lent){
if(!j||t[j]==t[i])next[++i]=++j;
else j=next[j];
}
}
注意那個小於號,注意邊界
- 利用:
匹配就走着,失配就/模式串/跳next
例一:返回第一次匹配的位置
int i=1,j=1;
while(i<=lens&&j<=lent){
if(!j||s[i]==t[j]) ++i,++j;
else j=next[j];
}
if(j>lent)printf("%d",i-lent);//j=lent+1,i=lens+1;起始位置i-lent
else printf("NO");
return 0;
例二:輸出每次匹配的位置(智障級的修改)
#include<bits/stdc++.h>
using namespace std;
const int NNN=1e5+10;
char s[NNN],t[NNN];
int next[NNN],ls,lt;
inline void get_next(){
int i=1,j=0;
while(i<lt){
if(!j||t[i]==t[j]) next[++i]=++j;
else j=next[j];
}
}
int main(){
scanf("%s",s+1);
scanf("%s",t+1);
ls=strlen(s+1);
lt=strlen(t+1);
get_next();
int i=1,j=1;
bool flag=false;
while(i<=ls&&j<=lt){
if(!j||s[i]==t[j]) ++i,++j;
else j=next[j];
if(j>lt)flag=true,printf("%d ",i-lt),j=1;
}
if(!flag)printf("NO");
return 0;
}
- 常數優化:
本蒟蒻是老萌新了,竟然現在才知道常數是什麼鬼
常數大概指你進行的步驟次數,或一個單位的時間複雜度
通過省略一些步驟來減少單位時間複雜度的方法叫做“卡常”,卡常的常見方法包括inline,register,i++變++i,常數優化等
那麼對於kmp,發現有一些比較是可以省去的
- 模式串/文本串匹配常數優化如下:
爲s指針,爲t指針
對於next數組,存在兩種情況:
對於一對失配的,一定有
顯然,可以常數優化
對於例二的優化:
int i=1,j=1;
bool flag=false;
while(i<=ls&&j<=lt){
if(!j||s[i]==t[j]) ++i,++j;
else{
if(t[j]==t[next[j]])j=next[next[j]];
j=next[j];
}
if(j>lt)flag=true,printf("%d ",i-lt),j=1;
}
if(!flag)printf("NO");
- 對於get_next的優化
發現的時候一定有
對於get_next的優化
inline void get_next(){
for(int i=1,j=0;i<lt;++i){
while(j&&t[i]!==t[j]) j=nex[j];
if(t[i+1]=t[j+1]) ++j;
nex[i+1]=j;
}
}
- 洛谷的板子
ACcode
#include<bits/stdc++.h>
using namespace std;
#define re register
const int NNN=1e6+10;
char a[NNN],b[NNN];
int nex[NNN],n,m;
inline void get_next(){
for(re int i=1,j=0;i<m;++i){
while(j&&b[i+1]!=b[j+1]) j=nex[j];
if(b[i+1]==b[j+1]) ++j;
nex[i+1]=j;
}
}
int main(){
scanf("%s%s",a+1,b+1);
n=strlen(a+1),m=strlen(b+1);
get_next();
for(re int i=1,j=0;i<=n;++i){
while(j&&a[i]!=b[j+1]) j=nex[j];
if(a[i]==b[j+1]) ++j;
if(j==m){
printf("%d\n",i-j+1);
j=nex[j];
}
}
for(re int i=1;i<=m;++i)printf("%d ",nex[i]);
return 0;
}
模式串上站在j考慮j+1