「NOI2018」 你的名字
作者:互联网
怕是以后在也不能做非思维题了,要不然都想不出来啥了
其实早知道是线段树合并,然后拍上一个维护 \(endpos\) 就开始坐以待毙了
Description
给定 \(S\) 和若干个询问,每个询问是 \(T,l,r\) 的形式,求 \(T\) 有多少个子串在 \(S[l\dots r]\) 中没有出现过
\(|S|\le 5\times 10^5,\sum |T|\le 10^6\)
其中对于 \(68pts\) 的部分分满足 \(l=1,r=|S|\)
Solution
首先考虑一个 \(n^2\) 的做法是对于每个 \(T\) 建 \(SAM\) 然后跑可达性统计,然后两个串一起跑,如果这个点在 \(T\) 里面能跑到 \(S\) 里面没有就加上可达的点
貌似也没啥优化的空间,那么换一个思路,往 后缀树 上面靠
直接统计还是不太行,那么正难则反,用总的 \(T\) 上的减掉公共子串
具体就是求一下 \(l[i]\) 表示 \(T\) 到 \(i\) 的前缀有多长可以在 \(S\) 上面作为一段后缀出现
如果如果不考虑存在重复的情况,就把 \(T\) 放到 \(S\) 上面跑,每次不行了就跳 \(fa\)
(这里深刻一个观点:后缀树上面点的深度基本上是没啥用的,含义就可以轻松得证)
因为 \(fa\) 里面的都是 \(x\) 的后缀,又要选最大值,所以每跳一下,长度减成现在点的 \(len\)
那么这就得到了一个 \(68pts\) 的做法
接着考虑怎么做不是全串的情况,先求所有的 \(endpos\) 集合
(如果对后缀树的相关熟练的话,其实这步是很好说的)
然后考虑对于 \([l\dots r]\) 的限制怎么写
然而并不能每次暴力重建,但是每次只是找儿子,跳父亲,找当前点在 \([l,r]\) 内的最大 \(len\)
第一个其实对应的是 \(endpos\) 有没有在 \([l,r]\) 之间有点
第二个是跳父亲,因为父亲的 \(endpos\) 是母集,随便跳
最后一个比较恶心,想清楚了是 \(min(len[i],now-max(now,pos)+1)\)
\(pos\) 是指在 \([l,r]\) 内的最大的 \(endpos\) 下标,这个可以用线段树求出来
还是一样的跑就行了
本文中加粗的部分是瓶颈,观察后缀自动机在什么时候用和跑 \(pos\) 这两个套路确实厉害
仿佛 \(CF\) 上有类似套路题?
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define reg register
namespace yspm{
inline int read(){
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=5e5+10,M=1e6+10,SZ=M*40;
int ls[SZ],rs[SZ],sum[SZ],tot,rt[M];
inline int max(int x,int y){return x>y?x:y;}
struct Saffix_Automation{
int son[M][26],len[M],fa[M],tot,las,id[M];
inline void extend(int x,int pos,bool fl){
int tmp=las,np=las=++tot; len[np]=len[tmp]+1; id[np]=pos;
while(!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
if(!tmp) return fa[np]=1,void();
int q=son[tmp][x]; if(len[q]==len[tmp]+1) return fa[np]=q,void();
int clone=++tot; len[clone]=len[tmp]+1; fa[clone]=fa[q]; fa[q]=fa[np]=clone;
for(reg int i=0;i<26;++i) son[clone][i]=son[q][i]; if(fl) id[clone]=id[np];
while(son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];
return ;
}
inline void clear(){
for(reg int i=1;i<=tot;++i) fa[i]=len[i]=id[i]=0,memset(son[i],0,sizeof(son[i])); las=tot=1;
return ;
}
inline void init(){tot=1; las=1; return;}
inline int num(int x){return len[x]-len[fa[x]];}
}S1,S2;
inline void push_up(int x){return sum[x]=sum[ls[x]]+sum[rs[x]],void();}
inline void upd(int &p,int l,int r,int pos){
if(!p) p=++tot;
if(l==r) return ++sum[p],void();
int mid=(l+r)>>1; if(pos<=mid) upd(ls[p],l,mid,pos); else upd(rs[p],mid+1,r,pos);
return push_up(p);
}
inline int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
if(l==r) return sum[x]|=sum[y],x;
int mid=(l+r)>>1,p=++tot;
ls[p]=merge(ls[x],ls[y],l,mid); rs[p]=merge(rs[x],rs[y],mid+1,r);
return push_up(p),p;
}
ll ans;
int head[M],cnt,mx[M],now,nowlen,len,slen; char s[M];
struct edge{int to,nxt;}e[M];
inline void add(int u,int v){
e[++cnt].to=v; e[cnt].nxt=head[u];
return head[u]=cnt,void();
}
inline void dfs(int x){
if(S1.id[x]) upd(rt[x],1,slen,S1.id[x]);
for(reg int i=head[x];i;i=e[i].nxt) dfs(e[i].to),rt[x]=merge(rt[x],rt[e[i].to],1,slen);
return ;
}
inline int query(int p,int l,int r,int st,int ed){
if(!p) return 0;
if(st<=l&&r<=ed) return sum[p];
int mid=(l+r)>>1;
if(st<=mid&&ls[p]&&query(ls[p],l,mid,st,ed)) return 1;
if(ed>mid&&rs[p]&&query(rs[p],mid+1,r,st,ed)) return 1;
return 0;
}
signed main(){
scanf("%s",s+1); slen=strlen(s+1); S1.init();
for(reg int i=1;i<=slen;++i) S1.extend(s[i]-'a',i,0);
for(reg int i=1;i<=S1.tot;++i) add(S1.fa[i],i); dfs(1);
int T=read(),l,r; while(T--){
S2.clear(); scanf("%s",s+1); len=strlen(s+1); l=read(); r=read();
for(reg int i=1;i<=len;++i) S2.extend(s[i]-'a',i,1);
for(reg int i=1;i<=len;++i) mx[i]=0; ans=0; now=1; nowlen=0;
for(reg int i=1;i<=len;++i){
while(1){
int nxt=S1.son[now][s[i]-'a'];
if(nxt&&query(rt[nxt],1,slen,l+nowlen,r)){nowlen++,now=nxt; break;}
if(!nowlen) break; --nowlen;
if(nowlen==S1.len[S1.fa[now]]) now=S1.fa[now];
} mx[i]=nowlen;
}
for(reg int i=2;i<=S2.tot;++i) ans+=max(0,S2.len[i]-max(S2.len[S2.fa[i]],mx[S2.id[i]]));
printf("%lld\n",ans);
}
return 0;
}
}
signed main(){return yspm::main();}
标签:tmp,return,int,len,fa,名字,np,NOI2018 来源: https://www.cnblogs.com/yspm/p/14186025.html