BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)

  二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans。建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节点。主席树进行二维数点即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,son[N][26],len[N],fail[N],id[N],f[N][19],p[N],size[N],dfn[N],root[N],tot,t,cnt=1,last=1;
struct data{int to,nxt;
}edge[N];
struct data2{int l,r,x;
}tree[N<<5];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
char s[N];
void ins(int c)
{
	int x=++cnt,p=last;last=x;len[x]=len[p]+1;id[len[x]]=x;
	while (!son[p][c]) son[p][c]=x,p=fail[p];
	if (!p) fail[x]=1;
	else
	{
		int q=son[p][c];
		if (len[p]+1==len[q]) fail[x]=q;
		else
		{
			int y=++cnt;
			len[y]=len[p]+1;
			memcpy(son[y],son[q],sizeof(son[q]));
			fail[y]=fail[q],fail[q]=fail[x]=y;
			while (son[p][c]==q) son[p][c]=y,p=fail[p];
		}
	}
}
void dfs(int k)
{
	dfn[k]=++tot;size[k]=1;
	for (int i=p[k];i;i=edge[i].nxt)
	{
		dfs(edge[i].to);
		size[k]+=size[edge[i].to];
	}
}
void add(int &k,int l,int r,int x)
{
	tree[++tot]=tree[k],k=tot;
	tree[k].x++;
	if (l==r) return;
	int mid=l+r>>1;
	if (x<=mid) add(tree[k].l,l,mid,x);
	else add(tree[k].r,mid+1,r,x);
}
bool query(int x,int y,int l,int r,int p,int q)
{
	if (!y) return 0;
	if (l==p&&r==q) return tree[y].x>tree[x].x;
	int mid=l+r>>1;
	if (q<=mid) return query(tree[x].l,tree[y].l,l,mid,p,q);
	else if (p>mid) return query(tree[x].r,tree[y].r,mid+1,r,p,q);
	else return query(tree[x].l,tree[y].l,l,mid,p,mid)|query(tree[x].r,tree[y].r,mid+1,r,mid+1,q);
}
void build()
{
	for (int i=2;i<=cnt;i++) addedge(fail[i],i);
	for (int i=1;i<=cnt;i++) f[i][0]=fail[i];f[1][0]=1;
	for (int j=1;j<19;j++)
		for (int i=1;i<=cnt;i++)
		f[i][j]=f[f[i][j-1]][j-1];
	dfs(1);tot=0;
	for (int i=1;i<=n;i++)
	{
		root[i]=root[i-1];
		add(root[i],1,cnt,dfn[id[i]]);
	}
}
bool check(int l,int r,int x,int ans)
{
	l=n-l+1,r=n-r+1,x=id[n-x+1];swap(l,r);
	for (int j=18;~j;j--) if (len[f[x][j]]>=ans) x=f[x][j];
	return query(root[l-1],root[r],1,cnt,dfn[x],dfn[x]+size[x]-1);
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#else
	const char LL[]="%lld\n";
#endif
	n=read(),m=read();
	scanf("%s",s+1);reverse(s+1,s+n+1);
	for (int i=1;i<=n;i++) ins(s[i]-'a');
	build();
	for (int i=1;i<=m;i++)
	{
		int a=read(),b=read(),c=read(),d=read();
		int l=0,r=min(b-a+1,d-c+1),ans=0;
		while (l<=r)
		{
			int mid=l+r>>1;
			if (check(a,b-mid+1,c,mid)) ans=mid,l=mid+1;
			else r=mid-1;
		}
		printf("%d\n",ans);
	}
	return 0;
}

  

posted @ 2019-05-04 19:58  Gloid  阅读(187)  评论(0编辑  收藏  举报