KMP 简略明晰讲稿(比你能找到的都要好)

本博客中令人费解的‘/’是用来/分割/信息区间/帮助理解/的

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,发现有一些比较是可以省去的
  1. 模式串/文本串匹配常数优化如下:
    ii为s指针,jj为t指针
    对于next数组,存在两种情况:t[k==t[next[k]],t[k]t[next[k]]t[k==t[next[k]],t[k]\neq t[next[k]]
    对于一对失配的i,ji,j,一定有s[i]t[j]s[i]\neq t[j]
    显然s[i]t[k]=t[next[k]]s[i]\neq t[k]=t[next[k]],可以常数优化

对于例二的优化:

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");
  1. 对于get_next的优化
    发现t[i]=t[j]t[i]=t[j]的时候一定有next[i]=next[j]next[i]=next[j]

对于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;
	}
}
#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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章