【 [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); } }