XII.CF1037H Security
一开始费尽心思写了个假的SA做法出来,后来才想到SAM做法……
我们考虑贪心地求出比当前询问的串 \(T\) 略大的串的方法:即先找有没有前 \(|T|\) 位全相同,第 \(|T|+1\) 位最小的串存在于 \([L,R]\) 中,如果没有再去找前 \(|T|-1\) 位全相同,第 \(|T|\) 位比 \(T\) 略大的串是否存在……
这样模拟的话,\(T\) 的每位上最多需要判断 \(26\) 个串是否存在,所以总计是 \(26|T|\) 的,假如有较快的判断是否出现的方法,应该可以通过。
我们考虑建出母串的SAM,然后将所有询问离线并按照右端点递增排序。当我们处理某一个询问时,我们可以保证有且仅有所有在 \(R_i\) 左侧的位置被加入了SAM(当然,这里不能动态构建SAM,因为我们接下来要上数据结构套在parent tree上,所以直接看作是在母串的SAM中所有需要的位置都被“点亮”即可)。
我们考虑在SAM上用串 \(T\) 按照我们上述暴力跑,则我们现在只需判断一个节点所对应的所有串中有无全部在 \([L,R]\) 范围内的串即可。很明显,因为我们已经保证只有 \(R\) 左侧的位置被“点亮”,所以我们仅需贪心地求出 \(\text{endpos}\) 集合中所有被点亮的结束位置中最右的那一个,并判断其对应的串是否在 \([L,R]\) 内即可。要动态维护一个 \(\text{endpos}\) 集合中有哪些数,考虑直接在 parent tree 的 dfs 序上套一棵线段树,就可以做到 \(O(\log n)\) 地查询集合中最大元素了。
于是总复杂度 \(O(26n\log n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,num[100100];
char s[100100];
int cnt=1;
struct Suffix_Automaton{int ch[26],len,fa;}t[200100];
int Add(int x,int c){
int xx=++cnt;t[xx].len=t[x].len+1;
for(;x&&!t[x].ch[c];x=t[x].fa)t[x].ch[c]=xx;
if(!x){t[xx].fa=1;return xx;}
int y=t[x].ch[c];
if(t[y].len==t[x].len+1){t[xx].fa=y;return xx;}
int yy=++cnt;t[yy]=t[y],t[yy].len=t[x].len+1;
t[xx].fa=t[y].fa=yy;
for(;x&&t[x].ch[c]==y;x=t[x].fa)t[x].ch[c]=yy;
return xx;
}
int dfn[200100],rev[200100],sz[200100],tot;
vector<int>v[200100];
void dfs(int x){dfn[x]=++tot,rev[tot]=x,sz[x]=1;for(auto y:v[x])dfs(y),sz[x]+=sz[y];}
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
int mx[800100];
void pushup(int x){mx[x]=max(mx[lson],mx[rson]);}
void modify(int x,int l,int r,int P,int val){
if(l>P||r<P)return;
if(l==r){mx[x]=val;return;}
modify(lson,l,mid,P,val),modify(rson,mid+1,r,P,val),pushup(x);
}
int query(int x,int l,int r,int L,int R){
if(l>R||r<L)return -1;
if(L<=l&&r<=R)return mx[x];
return max(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));
}
int L[200100],R[200100],len[200100],ord[200100],res[200100];
char ss[200100],*q[200100];
bool func(int id,int pos,int x){
// printf("%d %d %d\n",id,pos,x);
if(pos<len[id]&&t[x].ch[q[id][pos]-'a']&&func(id,pos+1,t[x].ch[q[id][pos]-'a']))return true;
for(int i=(pos<len[id]?q[id][pos]-'a'+1:0),y;i<26;i++)if((y=t[x].ch[i])&&(res[id]=query(1,1,tot,dfn[y],dfn[y]+sz[y]-1)-pos)>=L[id])return true;
return false;
}
int main(){
scanf("%s%d",s,&m),n=strlen(s);
for(int i=0,las=1;i<n;i++)num[i]=las=Add(las,s[i]-'a');
for(int i=2;i<=cnt;i++)v[t[i].fa].push_back(i);
// for(int i=2;i<=cnt;i++)printf("%d %d\n",t[i].fa,i);
// for(int i=0;i<n;i++)printf("%d ",num[i]);puts("");
dfs(1),memset(mx,-1,sizeof(mx));
q[1]=ss;
for(int i=1;i<=m;i++){
scanf("%d%d%s",&L[i],&R[i],q[i]),L[i]--,R[i]--,len[i]=strlen(q[i]);
q[i+1]=q[i]+len[i];
ord[i]=i;
}
// for(int i=1;i<=m;i++)printf("%d %d %s\n",L[i],R[i],q[i]);
sort(ord+1,ord+m+1,[](int x,int y){return R[x]<R[y];});
for(int i=0,j=1;i<n;i++){
modify(1,1,tot,dfn[num[i]],i);
// printf("RRR:%d\n",i);
for(;j<=m&&R[ord[j]]==i;j++){
// printf("QQQ:%d\n",ord[j]);
if(!func(ord[j],0,1))res[ord[j]]=-1;
}
}
for(int i=1;i<=m;i++){
if(res[i]==-1){puts("-1");continue;}
for(int j=0;j<=len[i];j++){
printf("%c",s[res[i]+j]);
if(s[res[i]+j]!=q[i][j])break;
}
puts("");
}
return 0;
}