P6640 [BJOI2020]封印 题解
对 \(T\) 建 SAM,拿 \(S\) 进去跑,可以得到每个 \(S\) 的前缀 \(S_i\) 的最长匹配后缀长度 \(sl_i\)。那么对于一个询问 \([l,r]\),答案就是 \(\max_{l\leq i\leq r}\{\min(sl_i,i-l+1)\}\)。
里面这个 \(\min\) 很难解决,我们考虑二分答案长度 \(mid\),这样就可以只询问 \([l+mid-1,r]\) 的 \(sl_i\) 的 \(\max\),这个使用 ST 表可以 \(O(1)\) 解决。总复杂度 \(O(n|\Sigma|\log n)\)。
点击查看代码
const int N=2e5+13;
namespace ST{
#define f _st
#define ccf log2
const int logN=21;
int f[N][logN],log2[N];
inline void init(int n){
for(int i=1;i<=n;++i) log2[i]=log2[i>>1]+1;
int k=log2[n];
for(int j=1;j<=k;++j)
for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r){
int k=log2[r-l+1]-1;
return max(f[l][k],f[r-(1<<k)+1][k]);
}
#undef f
#undef ccf
}
char s[N],t[N];
int nxt[N<<1],len[N<<1],ptot=1,lastpos=1;
int son[N<<1][2],zrzak[2];
inline int newpos(int nson[2],int nlen){return len[++ptot]=nlen,son[ptot][0]=nson[0],son[ptot][1]=nson[1],ptot;}
inline void insert(int c){
int p=lastpos,u=newpos(zrzak,len[p]+1);
while(p&&!son[p][c]) son[p][c]=u,p=nxt[p];
lastpos=u;
if(!p) return nxt[u]=1,void();
int d=son[p][c];
if(len[d]==len[p]+1) nxt[u]=d;
else{
int v=newpos(son[d],len[p]+1);
nxt[v]=nxt[d],nxt[u]=nxt[d]=v;
while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
}
}
int main(){
read(s+1),read(t+1);
int n=strlen(s+1),m=strlen(t+1);
for(int i=1;i<=m;++i) insert(t[i]-'a');
for(int i=1,now=1,l=0;i<=n;++i){
int c=s[i]-'a';
while(now&&!son[now][c]) now=nxt[now],l=len[now];
if(son[now][c]) now=son[now][c],++l;
ST::_st[i][0]=l;
}
ST::init(n);
int q;read(q);
while(q--){
int L,R;read(L),read(R);
if(!ST::query(L,R)){println(0);continue;}
int l=1,r=R-L+1;
while(l<r){
int mid=(l+r+1)>>1;
if(ST::query(L+mid-1,R)>=mid) l=mid;
else r=mid-1;
}
println(l);
}
return 0;
}