【 [Tjoi2016&Heoi2016]字符串】SAM+线段树合并+卡空间

卡空间卡空间卡空间orz orz orz orz
BZOJ4556(非权限)

4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 1454  Solved: 588 [Submit][Status][Discuss]

Description

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

Input

输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来
m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

Output

 对于每一次询问,输出答案。

Sample Input

5 5 aaaaa 1 1 1 5 1 5 1 1 2 3 2 3 2 4 2 3 2 3 2 4

Sample Output

1 1 2 2 2

HINT

Source

考虑倒序建立sam自动机,par树上倍增,和对每个结点开right数组线段树(线段树合并),之后询问[c,d]二分答案长度len然后在end[c]的倍增数组里面去找这个len长度。然后看这个结点的right线段树有没有包含[a,b-len+1]这个区间就可以了。 就这样。 卡空间啊卡空间,请务必将线段树空间开到3500005个结点(rgnoH学长好像之前OJ是动态空间过了,,实际重测却MLE),我反复MLE+RE,,, code:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
const int maxm = 3500005;
const int rt = 1;
int n,m;
int endc[maxn*2];
map<int,int>son[maxn*2];
int mx[maxn*2],par[maxn*2],las=1,tot=1,la[maxn*2],nt[maxn*2],en[maxn*2],owo;
void addedge(int a,int b) { en[++owo]=b; nt[owo]=la[a]; la[a]=owo; }
int cnt,ls[maxm],rs[maxm],znum[maxm],root[maxn*2];
int fa[maxn][20];
void insert(int l,int r,int &p,int zhi,int oo)
{
	p = ++cnt; znum[p]+=oo;
	if(l==r) return;
	int mid = (l+r)>>1;
	if(zhi>mid) insert(mid+1,r,rs[p],zhi,oo);
	else insert(l,mid,ls[p],zhi,oo);
}
int merge(int ll,int rr)
{
	if((!ll)||(!rr)) return ll+rr;
	int t = ++cnt; 
	ls[t] = merge(ls[ll],ls[rr]);
	rs[t] = merge(rs[ll],rs[rr]);
	znum[t] = znum[ls[t]] + znum[rs[t]];
	return t;
}
int push(int val) { mx[++tot] = val; return tot; }
void extend(int t,int k)
{
	int np,nq,p,q;
	np = push(mx[las]+1);
	for(p=las;p&&(!son[p][t]);p=par[p]) son[p][t]=np;
	if(!p) par[np] = rt;
	else
	{
		q = son[p][t];
		if(mx[p]+1==mx[q])
		{
			par[np] = q;
		}
		else
		{
			nq = push(mx[p]+1);
			son[nq] = son[q];
			par[nq] = par[q]; par[q] = par[np] = nq;
			for(;p&&son[p][t]==q;p=par[p]) son[p][t] = nq;
		}
	}
	las = np;
	endc[k] = las;
}
int dep[maxn*2];
void dfs(int x,int ba)
{
	if(!root[x]) root[x]=++cnt;
	fa[x][0]=ba; dep[x] = dep[ba]+1;
	for(int i=1;i<=18;i++) fa[x][i] = fa[fa[x][i-1]][i-1];
	for(int it=la[x];it;it=nt[it]) dfs(en[it],x),root[x]=merge(root[en[it]],root[x]);
}
char ss[maxn];
bool query(int p,int l,int r,int x,int y)
{
	if(!p) return false;
	if(x>y) return false;
	if(x<=l&&r<=y) return znum[p];
	 int mid =(l+r)>>1;
	if(y<=mid) return query(ls[p],l,mid,x,y);
	else if(x>mid) return query(rs[p],mid+1,r,x,y);
	else 
	{
		return query(ls[p],l,mid,x,y)|query(rs[p],mid+1,r,x,y);
	}
}
int fi(int x,int len)
{
	if(len>mx[par[x]]&&len<=mx[x]) return x;
	int k = 18;
	for(;k>=0;k--) if( mx[fa[x][k]]>=len ) x = fa[x][k];
	return x; 
}
bool isok(int len,int enen,int ll,int rr)
{
	int k = fi(endc[enen],len);
	return query(root[k],1,n,ll,rr);
}
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",ss+1);
	for(int i=n;i>=1;i--) extend(ss[i]-'a',i);
	for(int i=2;i<=tot;i++) addedge(par[i],i);
	for(int i=1;i<=n;i++) insert(1,n,root[endc[i]],i,1);
	dfs(1,0);
	for(int i=1;i<=m;i++)
	{
		int a,b,c,d,ans=0;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		int L=1,R=min((d-c+1),b-a+1);
		while(L<=R)
		{
			int mid = (L+R)>>1; 
			if(isok(mid,c,a,b-mid+1)) ans = mid , L=mid+1;
			else R=mid-1;
		}
		printf("%d\n",ans);
	}
}
 
posted @ 2018-06-03 14:59  Newuser233  阅读(7)  评论(0)    收藏  举报