[BJOI2020] 封印

一、题目

点此看题

二、解法

今天不知道为什么手感这么好,写一发完全没调就过掉了。

我感觉这种多组询问的字符串题是很难的,经常没有什么思路。我先考虑了一下能不能像 区间本质不同的子串个数 这样直接离线,但我想了很久发现不行的。

正确的做法是很天马行空的,我们 考虑预处理出 \(s\) 的所有子串在 \(t\) 中有没有出现 ,枚举是不可能枚举的,要把他们压在一起快速的处理。设 \(R[i]\)\([i,R[i]]\)\(t\) 中出现过,那么对于比 \(R[i]\) 小的右端点是一定出现过的,比 \(R[i]\) 大的右端点是没有出现过的。

\(R[i]\) 用后缀自动机可以快速处理,因为 \([i,R[i]]\) 是在 \(t\) 中出现过的,\([i+1,R[i]]\) 也是在 \(t\) 中出现过的,那么 \(R[i+1]\geq R[i]\),所以说我们可以暴力跳转移,当发现长度不适应后缀自动机上的这个点时,就可以跳后缀树上的父亲。不难发现时间复杂度是 \(O(n)\) 的。

知道了 \(R[i]\) 之后,询问 \((l,r)\) 的答案很容易写出来:

\[\max_{i=l}^r (\min(R[i],r)-i+1) \]

这个式子乍看上去没有办法优化,但别忘了我们还有一个法宝:离线 。如果你觉得里面的 \(\min\) 特别恶心那么我们可以分类讨论来去掉这个 \(\min\)

  • \(R[i]\leq r\),那么里面的柿子就变成了:\(R[i]-i+1\)
  • \(R[i]>r\),那么里面的柿子就变成了:\(r-i+1\)

拆掉 \(\min\) 之后问题变成了二维偏序之类的东西,解决他的固定套路就是 排序降维 。那么我们把 \(R[i],r\) 都从小到大排序,然后维护两颗线段树,一颗维护 \(-i\) 的最大值,一颗维护 \(R[i]-i+1\) 就可以了。

时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 200005;
const int inf = -1e9;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,cnt,last,R[M],ans[M];char s[M],t[M];
int mx[2][4*M];
struct node
{
	int fa,len,ch[2];
}a[2*M];
struct reg//regret
{
	int l,r,id;
	bool operator < (const reg &R) const
	{
		return r<R.r;
	}
}b[M],q[M];
void add(int c)
{
	int p=last,np=last=++cnt;
	a[np].len=a[p].len;
	for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
	if(!p) a[np].fa=1;
	else
	{
		int q=a[p].ch[c];
		if(a[p].len+1==a[q].len) a[np].fa=q;
		else
		{
			int nq=++cnt;a[nq]=a[q];
			a[nq].len=a[p].len+1;
			a[np].fa=a[q].fa=nq;
			for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
		}
	}
}
void ins(int i,int l,int r,int id,int v,int f)
{
	if(l==r)
	{
		mx[f][i]=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=id) ins(i<<1,l,mid,id,v,f);
	else ins(i<<1|1,mid+1,r,id,v,f);
	mx[f][i]=max(mx[f][i<<1],mx[f][i<<1|1]);
}
int ask(int i,int l,int r,int L,int R,int f)
{
	if(L>r || l>R) return inf;
	if(L<=l && r<=R) return mx[f][i];
	int mid=(l+r)>>1;
	return max(ask(i<<1,l,mid,L,R,f),ask(i<<1|1,mid+1,r,L,R,f));
}
signed main()
{
	scanf("%s %s",s+1,t+1);
	n=strlen(s+1);m=strlen(t+1);
	cnt=last=1;//attention
	for(int i=1;i<=m;i++)
		add(t[i]-'a');
	for(int i=1,p=1;i<=n;i++)
	{
		int r=min(i-1,R[i-1]);
		if(r==i-1) p=1;
		while(p!=1 && a[a[p].fa].len>r-i+1) p=a[p].fa;
		while(r<n && a[p].ch[s[r+1]-'a'])
		{
			r++;
			p=a[p].ch[s[r]-'a'];
		}
		R[i]=r;
		b[i]=reg{i,r,0};
	}
	sort(b+1,b+1+n);
	k=read();
	for(int i=1;i<=k;i++)
	{
		int l=read(),r=read();
		q[i]=reg{l,r,i};
	}
	sort(q+1,q+1+k);
	memset(mx,-0x3f,sizeof mx);
	for(int i=1;i<=n;i++)
		ins(1,1,n,i,-i,0);
	for(int i=1,j=1;i<=k;i++)
	{
		int l=q[i].l,r=q[i].r,id=q[i].id;
		while(j<=n && b[j].r<=r)
		{
			ins(1,1,n,b[j].l,inf,0);
			ins(1,1,n,b[j].l,b[j].r-b[j].l+1,1);
			j++;
		}
		ans[id]=max(ask(1,1,n,l,r,1),r+ask(1,1,n,l,r,0)+1);
		ans[id]=max(0,ans[id]);
	}
	for(int i=1;i<=k;i++)
		printf("%d\n",ans[i]);
}
posted @ 2020-12-23 17:04  C202044zxy  阅读(98)  评论(0编辑  收藏  举报