【题解】封印 [BJOI2020] [P6640] [Loj3298]
【题解】封印 [BJOI2020] [P6640] [Loj3298]
传送门:封印 \(\text{[BJOI2020] [P6640]}\) \(\text{[Loj3298]}\)
【分析】
\(\text{CF}\) 上有一堆这种类似的题。
按照套路,先求出 \(s\) 中以每个 \(i\) 作为右端点与 \(t\) 能匹配到的最长子串长度,记为 \(len(i)\)(用\(\text{SA}\) 和 \(\text{SAM}\) 均可)。
那么对于一次询问,答案为 \(\max_{i=l}^{r}\{i-\max(l,i-len(i)+1)+1\}\),似乎不太好维护,但注意到 \(i-len(i)+1\) 是有单调性的(单调不减),也就是说我们可以把 \([l,r]\) 中的所有 \(i\) 分为两半,左边为 \(\max_{i=l}^{mid-1}\{i-l+1\}=mid-1-l+1\),右边为 \(\max_{i=mid}^{r}\{len(i)\}\),套个静态查询区间最大值的数据结构进行维护。求 \(mid\) 也很简单,把询问离线按 \(l\) 排序,一个指针扫过去就完了。如果强制在线就二分求 \(mid\) 。
写了个两只 \(\log\) 的树状数组跑得飞快(雾)。
【Code】
#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=2e5+3;
int n,T,len[N],Ans[N];char s[N],t[N];
struct QAQ{int l,r,id;inline bool operator<(const QAQ &O)const{return l<O.l;}}Q[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Suffix_Automaton{
int O,last,link[N<<1],maxlen[N<<1],trans[N<<1][2];
Suffix_Automaton(){last=O=1;}
inline void insert(Re ch){
Re z=++O,p=last;maxlen[z]=maxlen[p]+1;
while(p&&!trans[p][ch])trans[p][ch]=z,p=link[p];
if(!p)link[z]=1;
else{
Re x=trans[p][ch];
if(maxlen[x]==maxlen[p]+1)link[z]=x;
else{
Re y=++O;maxlen[y]=maxlen[p]+1;
for(Re i=0;i<2;++i)trans[y][i]=trans[x][i];
while(p&&trans[p][ch]==x)trans[p][ch]=y,p=link[p];
link[y]=link[x],link[x]=link[z]=y;
}
}
last=z;
}
inline void build(){
for(Re i=1;t[i];++i)insert(t[i]-'a');
for(Re i=1,p=1,Len=0;i<=n;++i){
Re a=s[i]-'a';
while(p&&!trans[p][a])p=link[p],Len=maxlen[p];
if(!p)p=1,Len=0;
else p=trans[p][a],++Len;
len[i]=min(Len,i);
}
}
}SAM;
struct BIT{
int C[N];
inline void add(Re x,Re v){while(x<=n)C[x]=max(C[x],v),x+=x&-x;}
inline int ask(Re l,Re r){
Re ans=0;
while(l<=r){
while(r-(r&-r)>=l)ans=max(ans,C[r]),r-=r&-r;
ans=max(ans,len[r--]);
}
return ans;
}
}TR;
int main(){
// freopen("123.txt","r",stdin);
scanf("%s%s",s+1,t+1),n=strlen(s+1);
in(T),SAM.build();
for(Re i=1;i<=n;++i)TR.add(i,len[i]);
for(Re i=1;i<=T;++i)in(Q[i].l),in(Q[i].r),Q[i].id=i;
sort(Q+1,Q+T+1);
for(Re l=1,i=1,o=1;l<=n&&o<=T;++l){
while(i<=n&&i-len[i]+1<l)++i;//找到第一个满足i-len[i]+1>=l的位置
while(o<=T&&Q[o].l==l){
if(i-1>=l)Ans[Q[o].id]=min(i-1,Q[o].r)-l+1;
if(i<=Q[o].r)Ans[Q[o].id]=max(Ans[Q[o].id],TR.ask(max(i,l),Q[o].r));
++o;
}
}
for(Re i=1;i<=T;++i)printf("%d\n",Ans[i]);
}