本博客中令人费解的‘/’是用来/分割/信息区间/帮助理解/的
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