其他分享
首页 > 其他分享> > 扩展KMP学习小记

扩展KMP学习小记

作者:互联网

简介

扩展KMP(又称EXKMP)是干嘛的?

大概目前已知的可以处理一个字符串和每个后缀的LCP长度,或者匹配串和被匹配串的LCP长度。

做法

先以求出一个字符串和自己后缀的LCP为例。

我们从小到大按位处理,如果暴力往后跑匹配是\(O(n^2)\)的,不能通过。

如果我们记录了之前已经匹配好的区间中,右端点最大的区间\([L,R]\)。那么如果这个区间右端点包含了当前要处理的这个点\(i\),那么我们显然可以初始化\(z_i=\min(z_{L-i+1},R-i+1)\)。然后之后暴力匹配并更新\([L,R]\),容易发现每成功暴力匹配一次,\(R+1\),则复杂度为\(O(n)\)。

事实上个人觉得这个思想和manacher特别像。

如果要求匹配串和被匹配串的LCP长度,那么直接将被匹配串接在匹配串后面,加个特殊字符就可以跑了。

板子题的code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (40000000+5)
#define M (6000000+5)
#define K (1500+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,H,F[N],L,R;char s1[N],s2[N],c[N];ll ToT;
int main(){
	freopen("1.in","r",stdin);
	int i,j;scanf("%s%s",s1+1,s2+1);n=strlen(s1+1);m=strlen(s2+1);for(i=1;i<=m;i++) c[++H]=s2[i];c[++H]='#';for(i=1;i<=n;i++) c[++H]=s1[i];
	for(i=2;i<=H;i++){if(R>=i) F[i]=min(F[i-L+1],R-i+1);while(c[i+F[i]]==c[F[i]+1]) F[i]++;i+F[i]-1>R&&(L=i,R=i+F[i]-1);}F[1]=m;
	for(i=1;i<=m;i++) ToT^=1ll*i*(F[i]+1);printf("%lld\n",ToT);ToT=0;for(i=m+2;i<=H;i++) ToT^=1ll*(i-m-1)*(F[i]+1);printf("%lld\n",ToT); 
}

例题:[NOIP2020] 字符串匹配

这看上去是唯一能找到的例题。

首先我们有一个暴力\(O(n(\ln n+26))\)的东西,枚举\(AB\)串长度,然后往上倍增枚举循环到哪里,哈希判断即可。然后暴力前缀和数点。

首先我们先考虑去掉这个\(O(26n)\)。

设循环节长度为\(Len\),当前循环次数为\(k\),若\(2\mid k\),则\(C\)中奇数字符出现次数等于原串奇数字符出现次数,可以直接维护。

若\(2\nmid k\),则\(C\)中奇数字符出现次数等于\([Len+1,n]\)奇数字符出现次数,因为每次变化量\(O(1)\),故可以\(O(n)\)维护出来。

然后我们现在的问题是:每个位置开始往后能循环几次。

我们考虑对原串跑一边EXKMP,则对于一个位置\(i\),\([1,i-1]\)的循环次数显然为\(\frac{z_i}{i-1}+1\),也可以做到\(O(1)\)。

因此我们可以在\(O(Tn)\)复杂度内解决这个问题。

code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N ((1<<20)+5)
#define M (6000000+5)
#define K (1500+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int T,n,x,y,z,F[N],L,R,Pf[150],Ps,Fl[150],G[150],Fs,P1,P2,Pt,Ct;ll ToT;char A[N];
I void Solve(){
	Me(F,0);Me(G,0);Me(Fl,0);Me(Pf,0);Ct=0;int i;scanf("%s",A+1);n=strlen(A+1);L=R=0;for(i=2;i<=n;i++) {if(i<=R) F[i]=min(F[i-L+1],R-i+1);while(A[i+F[i]]==A[F[i]+1]) F[i]++;if(i+F[i]-1>R) L=i,R=i+F[i]-1;}
	for(i=1;i<=n;i++) Pf[A[i]]^=1;for(i='a';i<='z';i++) Ct+=Pf[i];Ps=Ct;Me(Fl,0);P1=P2=ToT=Fs=0;for(i=2;i<=n;i++){
		Pf[A[i-1]]^=1;Pf[A[i-1]]?(Ps++,P2+=G[Ps]):(P2-=G[Ps],Ps--);if(i>2)x=min(F[i]+i-1,n-1)/(i-1),ToT+=1ll*(x+1)/2*P2+x/2*P1;Fl[A[i-1]]^=1;Fl[A[i-1]]?Fs++:Fs--;G[Fs]++;P1+=(Fs<=Ct);P2+=(Fs<=Ps);
	}printf("%lld\n",ToT);
}
int main(){
	freopen("1.in","r",stdin);
	scanf("%d",&T);while(T--) Solve();
}

标签:匹配,min,db,ll,扩展,long,KMP,小记,define
来源: https://www.cnblogs.com/275307894a/p/16475477.html