[HEOI2016/TJOI2016]字符串
字符串
题解
挺好想的一道题。
很明显,对于一个询问,我们是可以去二分最长公共前缀的最大值的。
当长度
l
l
l满足是,长度
l
−
1
l-1
l−1显然也满足条件,是具有单调性的。
至于如何判断长度
m
i
d
mid
mid合不合法,考虑SA。
显然,
m
i
d
mid
mid合法时,
c
c
c显然与
[
a
,
a
−
m
i
d
+
1
]
[a,a-mid+1]
[a,a−mid+1]之间某个位置有不低于
m
i
d
mid
mid的lcp。
因为对于排序后的SA数组,相邻相邻两个字符串的lcp最大,向外逐渐减小。
所以我们可以先将SA建出来,排完序后,与
c
c
c有长度不低于
m
i
d
mid
mid的lcp的串一定是一个包含串
c
c
c连续的区间。
我们可以去二分这个区间的左右端点,看这个区间中有没有
[
a
,
a
−
m
i
d
+
1
]
[a,a-mid+1]
[a,a−mid+1]开始的字符串即可。
因为左右端点二分时需要求中间一段区间
h
e
i
g
h
t
height
height数组的最小值,而采用线段树的话会达到3个
l
o
g
log
log,我们只能采用st表去维护
h
e
i
g
h
t
height
height数组,以做到
O
(
1
)
O(1)
O(1)查询。
至于区间中寻找有没有
[
a
,
a
−
m
i
d
+
1
]
[a,a-mid+1]
[a,a−mid+1]开头的字符串,可以使用主席树,也是很常规的操作。
时间复杂度 O ( n l o g 2 n ) O\left(nlog^2n\right) O(nlog2n)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
#define MAXN 3000005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,q,x[MAXN],y[MAXN],c[MAXN],z[MAXN],lg[MAXN];
int sa[MAXN],rk[MAXN],hei[MAXN],st[25][MAXN];char str[MAXN];
void getSa(){
for(int i=1;i<=n;i++)c[x[i]=str[i]]++;
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>0;i--)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]]++,z[i]=x[i];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>0;i--)sa[c[x[y[i]]]--]=y[i];
for(int i=1;i<=n;i++)x[i]=y[i]=0;x[sa[1]]=1;num=1;
for(int i=2;i<=n;i++)x[sa[i]]=(z[sa[i]]==z[sa[i-1]]&&z[sa[i]+k]==z[sa[i-1]+k])?num:++num;
if(num==n)break;m=num;
}
}
void getHi(){
int k=0;for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1;i<=n;i++){
if(k)k--;int j=sa[rk[i]-1];
while(str[i+k]==str[j+k])k++;hei[rk[i]]=k;
}
}
void getSt(){
for(int j=2;j<=n;j++)lg[j]=lg[j>>1]+1;
for(int i=0;i<n;i++)st[0][i]=hei[i+1];
for(int i=1;i<=lg[n];i++)
for(int j=0;j<n-(1<<i-1);j++)
st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
int rmq(int l,int r){return min(st[lg[r-l]][l],st[lg[r-l]][r-(1<<lg[r-l])]);}
struct ming{int val,lson,rson;};
class Tree{
private:
ming tr[MAXN*40];int tot,root[MAXN];
void insert(int las,int &now,int l,int r,int ai){
tr[now=++tot]=tr[las];tr[now].val++;if(l==r)return;int mid=l+r>>1;
if(ai<=mid)insert(tr[las].lson,tr[now].lson,l,mid,ai);
else insert(tr[las].rson,tr[now].rson,mid+1,r,ai);
}
int query(int las,int now,int l,int r,int al,int ar){
if(l>r||l>ar||r<al)return 0;
if(al<=l&&r<=ar)return tr[now].val-tr[las].val;int mid=l+r>>1,res=0;
if(al<=mid)res+=query(tr[las].lson,tr[now].lson,l,mid,al,ar);
if(ar>mid)res+=query(tr[las].rson,tr[now].rson,mid+1,r,al,ar);
return res;
}
public:
void build(){root[0]=++tot;for(int i=1;i<=n;i++)insert(root[i-1],root[i],1,n,sa[i]);}
int ask(int l,int r,int al,int ar){return query(root[l-1],root[r],1,n,al,ar);}
}T;
bool check(int a,int b,int c,int x){
int l=1,r=rk[c],al,ar;while(l<r){int mid=l+r>>1;if(rmq(mid,rk[c])<x)l=mid+1;else r=mid;}
al=l;l=rk[c],r=n;while(l<r){int mid=l+r+1>>1;if(rmq(rk[c],mid)<x)r=mid-1;else l=mid;}ar=r;
return T.ask(al,ar,a,b)>0;
}
signed main(){
read(n);read(q);scanf("%s",str+1);m=126;
getSa();getHi();getSt();T.build();
for(int i=1;i<=q;i++){
int a,b,c,d;read(a);read(b);read(c);read(d);
int l=0,r=min(b-a+1,d-c+1);
while(l<r){
int mid=l+r+1>>1;
if(check(a,b-mid+1,c,mid))l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}

浙公网安备 33010602011771号