cunzai_zsy0531

关注我

P6640 [BJOI2020]封印 题解

题面

\(T\) 建 SAM,拿 \(S\) 进去跑,可以得到每个 \(S\) 的前缀 \(S_i\) 的最长匹配后缀长度 \(sl_i\)。那么对于一个询问 \([l,r]\),答案就是 \(\max_{l\leq i\leq r}\{\min(sl_i,i-l+1)\}\)

里面这个 \(\min\) 很难解决,我们考虑二分答案长度 \(mid\),这样就可以只询问 \([l+mid-1,r]\)\(sl_i\)\(\max\),这个使用 ST 表可以 \(O(1)\) 解决。总复杂度 \(O(n|\Sigma|\log n)\)

点击查看代码
const int N=2e5+13;
namespace ST{
#define f _st
#define ccf log2
const int logN=21;
int f[N][logN],log2[N];
inline void init(int n){
	for(int i=1;i<=n;++i) log2[i]=log2[i>>1]+1;
	int k=log2[n];
	for(int j=1;j<=k;++j)
		for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r){
	int k=log2[r-l+1]-1;
	return max(f[l][k],f[r-(1<<k)+1][k]);
}
#undef f
#undef ccf
}
char s[N],t[N];
int nxt[N<<1],len[N<<1],ptot=1,lastpos=1;
int son[N<<1][2],zrzak[2];
inline int newpos(int nson[2],int nlen){return len[++ptot]=nlen,son[ptot][0]=nson[0],son[ptot][1]=nson[1],ptot;}
inline void insert(int c){
	int p=lastpos,u=newpos(zrzak,len[p]+1);
	while(p&&!son[p][c]) son[p][c]=u,p=nxt[p];
	lastpos=u;
	if(!p) return nxt[u]=1,void();
	int d=son[p][c];
	if(len[d]==len[p]+1) nxt[u]=d;
	else{
		int v=newpos(son[d],len[p]+1);
		nxt[v]=nxt[d],nxt[u]=nxt[d]=v;
		while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
	}
}
int main(){
	read(s+1),read(t+1);
	int n=strlen(s+1),m=strlen(t+1);
	for(int i=1;i<=m;++i) insert(t[i]-'a');
	for(int i=1,now=1,l=0;i<=n;++i){
		int c=s[i]-'a';
		while(now&&!son[now][c]) now=nxt[now],l=len[now];
		if(son[now][c]) now=son[now][c],++l;
		ST::_st[i][0]=l;
	}
	ST::init(n);
	int q;read(q);
	while(q--){
		int L,R;read(L),read(R);
		if(!ST::query(L,R)){println(0);continue;}
		int l=1,r=R-L+1;
		while(l<r){
			int mid=(l+r+1)>>1;
			if(ST::query(L+mid-1,R)>=mid) l=mid;
			else r=mid-1;
		}
		println(l);
	}
	return 0;
}
posted @ 2022-05-18 21:08  cunzai_zsy0531  阅读(23)  评论(0编辑  收藏  举报