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

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