Luogu P4094 [HEOI2016/TJOI2016]字符串
Link
考虑二分答案\(lim\)。
那么我们现在要求的是是否存在\(i\in[a,b],s.t. lcp(suf_i,suf_c)\ge lim\)。
从\(rand_c\)开始往两端扩展到\([l,r]\),满足\(\forall i\in(l,r],h_i\ge mid\)。
那么我们要求的就是在\(sa_l,\cdots,sa_r\)中是否存在一个属于\([a,b]\)的数。
这是一个典型的二位数点问题,可以用主席树解决。
而要求\(l,r\)可以预处理RMQ+倍增。
时间复杂度\(O(n\log n+m\log^2n)\)。
#include<cstdio>
#include<cctype>
#include<numeric>
#include<cstring>
#include<algorithm>
const int N=100007;
char str[N+10];
int n,q,cnt,sa[N],rank[N],h[N],s[N<<1],t[N<<1],pos[N<<1],r[N>>1],c[N>>1],f[17][N],root[N],ls[N<<5],rs[N<<5],sum[N<<5];
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
#define is(x,a) sa[r[s[x]]a]=x
#define IS(S) \
memset(sa+1,0,n<<2),memset(c+1,0,m<<2); \
for(int i=1;i<=n;++c[s[i++]]); \
std::partial_sum(c+1,c+m+1,c+1),memcpy(r+1,c+1,m<<2); \
for(int i=N;i;--i) is(S[i],--); \
for(int i=1;i<=m;++i) r[i]=c[i-1]+1; \
for(int i=1;i<=n;++i) if(sa[i]>1&&t[sa[i]-1]) is(sa[i]-1,++); \
memcpy(r+1,c+1,m<<2); \
for(int i=n;i;--i) if(sa[i]>1&&!t[sa[i]-1]) is(sa[i]-1,--);
void SAIS(int n,int m,int*s,int*t,int*pos)
{
int N=0,M=rank[1]=0,*S=s+n;
t[n]=0;
for(int i=n-1;i;--i) t[i]=s[i]^s[i+1]? s[i]>s[i+1]:t[i+1];
for(int i=2;i<=n;++i) rank[i]=t[i-1]&&!t[i]? pos[++N]=i,N:0;
IS(pos);
for(int i=1,x,y=0;i<=n;++i)
if((x=rank[sa[i]]))
{
if(M<=1||pos[x+1]-pos[x]!=pos[y+1]-pos[y]) ++M;
else for(int a=pos[x],b=pos[y];a<=pos[x+1];++a,++b) if((s[a]<<1|t[a])^(s[b]<<1|t[b])) {++M;break;}
S[y=x]=M;
}
if(M^N) SAIS(N,M,S,t+n,pos+n); else for(int i=1;i<=N;++i) sa[S[i]]=i;
for(int i=1;i<=N;++i) S[i]=pos[sa[i]];
IS(S);
}
#define mid ((l+r)>>1)
void update(int&x,int p,int l,int r,int t)
{
sum[x=++cnt]=sum[p]+1,ls[x]=ls[p],rs[x]=rs[p];
if(l==r) return ;
t<=mid? update(ls[x],ls[p],l,mid,t):update(rs[x],rs[p],mid+1,r,t);
}
int query(int x,int p,int l,int r,int L,int R)
{
if(sum[x]==sum[p]||L>r||R<l) return 0;
if(L<=l&&r<=R) return sum[x]-sum[p];
return query(ls[x],ls[p],l,mid,L,R)+query(rs[x],rs[p],mid+1,r,L,R);
}
#undef mid
int check(int a,int b,int c,int lim)
{
int l=rank[c],r=rank[c];
for(int i=16,x;~i;--i) if(l>(x=1<<i)&&f[i][l-x+1]>=lim) l-=x;
for(int i=16,x;~i;--i) if(r+(x=1<<i)<=n&&f[i][r+1]>=lim) r+=x;
return query(root[l-1],root[r],1,n,a,b-lim+1);
}
int main()
{
n=read(),q=read(),scanf("%s",str+1);
for(int i=1;i<=n;++i) s[i]=str[i]-'a'+2;
s[++n]=1,SAIS(n--,27,s,t,pos);
for(int i=1;i<=n;++i) rank[sa[i]=sa[i+1]]=i;
for(int i=1,j,l=0;i<=n;h[rank[i++]]=l) for(j=sa[~-rank[i]],l-=l>0;i+l<=n&&j+l<=n&&s[i+l]==s[j+l];++l);
memcpy(f[0]+1,h+1,n<<2);
for(int j=1;j<17;++j) for(int i=1,d=1<<(j-1);i+d<=n;++i) f[j][i]=std::min(f[j-1][i],f[j-1][i+d]);
for(int i=1;i<=n;++i) update(root[i],root[i-1],1,n,sa[i]);
for(int a,b,c,d,l,r,ans;q;--q)
{
a=read(),b=read(),c=read(),d=read(),l=ans=0,r=d-c+1;
for(int mid;l<=r;) check(a,b,c,mid=(l+r)>>1)? ans=mid,l=mid+1:r=mid-1;
printf("%d\n",ans);
}
}