您的位置:首页 > 其它

bzoj 4556 [Tjoi2016&Heoi2016]字符串

2017-06-22 12:02 337 查看
4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec Memory Limit: 128 MB

Submit: 952 Solved: 374

[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

【分析】

看阿当学长的代码肝还肝了两个多小时 2333334666666

我们发现这道鬼畜的题用LCP不好搞,于是翻转一下,变成了求LCS蛤蛤。

把串翻转以后建出来一只后缀自动机,搞出来pre树。

我们惊奇的发现如果区间[a,b]中某个节点和d的节点在pre树上的LCA的深度为dep,那么dep可以来更新答案。

显然不能枚举[a,b]区间里每一个数,那么我们二分答案。

发现其实[c,d]串如果考虑LCS的话c没有什么卵用,只是用来卡答案上界的。设当前答案为 mid,那么我们从d所在pre树中的位置向上蹦跶到深度最小且满足 step>=mid 的位置,然后如果这个节点的子树中有[a,b]区间对应在pre树上的节点,那么该答案成立。判断可以通过权值线段树的合并来实现。

总复杂度 O(nlog2n)

我一直在想一个问题,为什么Markdown里面的代码片这么丑呢。

【代码】

//bzoj 4556 字符串
#include<bits/stdc++.h>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int M=4000005;
const int mxn=200005;
char s[mxn];
int ls[M],rs[M];
int m,T,len,tot,p,q,np,nq,size;
int b[mxn],C[mxn],root[mxn],pos[mxn],step[mxn],son[mxn][28],pre[mxn][22];
inline void insert(int &x,int l,int r,int v)
{
x=(++size);
int mid=l+r>>1;
if(l==r) return;
if(v<=mid) insert(ls[x],l,mid,v);
else insert(rs[x],mid+1,r,v);
}
inline int merge(int x,int y)
{
if(!x) return y;
if(!y) return x;
int z=(++size);
ls[z]=merge(ls[x],ls[y]);
rs[z]=merge(rs[x],rs[y]);
return z;
}
inline bool find(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(l==L && r==R) return 1;
int mid=l+r>>1;
if(R<=mid) return find(ls[x],l,mid,L,R);
else if(L>mid) return find(rs[x],mid+1,r,L,R);
else return find(ls[x],l,mid,L,mid)||find(rs[x],mid+1,r,mid+1,R);
}
inline void sam()
{
int i,j,c;
np=tot=1;
fo(i,1,len)
{
c=s[i]-'a'+1,p=np;
step[np=(++tot)]=step[p]+1;
pos[i]=np,insert(root[np],1,len,i);
while(p && !son[p][c])
son[p][c]=np,p=pre[p][0];
if(!p) {pre[np][0]=1;continue;}
q=son[p][c];
if(step[q]==step[p]+1)
pre[np][0]=q;
else
{
step[nq=(++tot)]=step[p]+1;
memcpy(son[nq],son[q],sizeof son[q]);
pre[nq][0]=pre[q][0];
pre[q][0]=pre[np][0]=nq;
while(p && son[p][c]==q)
son[p][c]=nq,p=pre[p][0];
}
}
fo(i,1,tot) b[step[i]]++;
fo(i,1,tot) b[i]+=b[i-1];
fo(i,1,tot) C[b[step[i]]--]=i;
for(i=tot;i>=1;i--)
{
int x=C[i],fa=pre[x][0];
root[fa]=merge(root[fa],root[x]);
}
fo(j,1,20) fo(i,1,tot) pre[i][j]=pre[pre[i][j-1]][j-1];
}
inline bool check(int mid,int x,int l,int r)
{
for(int i=20;i>=0;i--) if(step[pre[x][i]]>=mid) x=pre[x][i];
return find(root[x],1,len,l,r);
}
int main()
{
int i,j,a,b,c,d;
scanf("%d%d",&len,&m);
scanf("%s",s+1);
reverse(s+1,s+len+1);
sam();
while(m--)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
a=len-a+1,b=len-b+1,c=len-c+1,d=len-d+1;
swap(a,b),swap(c,d);
int l=0,r=min(d-c+1,b-a+1);
while(l<r)
{
int mid=(l+r>>1)+1;
if(check(mid,pos[d],a+mid-1,b)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: