NOI2018 你的名字
终于将后缀自动机,后缀树理解了。
学习了后缀自动机转后缀树,真的不用后缀数组了。
68分就是广义后缀自动机的思路。
我直接在原串的自动机上插一边询问串,再逐个恢复即可。这样是线性的。
剩下的就是区间询问了。
对于询问串的每个$l$,满足右端点在$[pos+1,n]$的串不在$S$中出现的是一个区间。
并且随着$l$的增加$pos$单调移动。
这样我们就可以求出可重的在$S$中未出现的串。
然后用后缀数组的$height$去掉重复统计的就行了。
反串的后缀自动机的$fail$树是正串的后缀树。
挺好理解的,但是注意后缀树中每个后缀都变成了$S[i+1,n]*$。
即保证了每个后缀都有一个不同的节点。
建出后缀树之后就可以按照边的大小顺序$dfs$得到所有后缀的排名了。
我一开始在疑惑怎么比较后缀树节点每个出边代表字符串的大小。
出边都不同了第一个字符肯定不同啊。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 1000005 #define N 20000005 int n,m; char s[M]; vector <int> to[M]; int sz,L[M],R[M],a[M]; int h[M],num,root[M],ch[N][2]; struct Suffix_Automaton { int las,node_num; int mx[M],fa[M],pos[M],nxt[M][26]; inline void init() { las=node_num=1; } inline void clear() { las=0; for1(1,node_num,i) { mx[i]=fa[i]=pos[i]=0; memset(nxt[i],0,sizeof(nxt[i])); } node_num=0; } inline void extend_(int c) { int p=las,np=las=++node_num; mx[np]=mx[p]+1; while (p&&!nxt[p][c]) nxt[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=nxt[p][c]; if(mx[q]==mx[p]+1) fa[np]=q; else { int nq=++node_num; mx[nq]=mx[p]+1; memcpy(nxt[nq],nxt[q],sizeof(nxt[q])); fa[nq]=fa[q],fa[q]=fa[np]=nq; while (nxt[p][c]==q) nxt[p][c]=nq,p=fa[p]; } } } }sam,Y; struct Suffix_Array { int f[M],g[M],cnt; int sa_[M],rank_[M]; int pos[M],buc[M],sta[M]; vector <pair<int,int> >vis[M]; inline void dfs(int x) { int size=vis[x].size(); if(pos[x]) { ++cnt; sa_[cnt]=m-Y.mx[x]+1; rank_[m-Y.mx[x]+1]=cnt; } for1(1,size,i) dfs(vis[x][i-1].second); } inline void build() { Y.clear(),Y.init(); FOR2(m,1,i) Y.extend_(s[i]-'a'),f[Y.las]=m-i+1,pos[Y.las]=1; for1(1,Y.node_num,i) ++buc[Y.mx[i]]; for1(1,m,i) buc[i]+=buc[i-1]; for1(1,Y.node_num,i) sta[buc[Y.mx[i]]--]=i; FOR2(Y.node_num,1,i) f[Y.fa[sta[i]]]=f[sta[i]]; for1(2,Y.node_num,i) vis[Y.fa[i]].push_back(make_pair(s[m-f[i]+Y.mx[Y.fa[i]]+1],i)); for1(1,Y.node_num,i) sort(vis[i].begin(),vis[i].end()); dfs(1); cnt=0; for1(1,Y.node_num,i) buc[i]=pos[i]=0,vis[i].clear(); } inline ll calc_() { int x=0; ll ans=0; for1(1,m,i) { while (s[i+x]==s[sa_[rank_[i]-1]+x]) ++x; g[rank_[i]]=x,x-=x!=0; } for1(1,m,i) { x=sa_[i]; if(x+g[i]-1>h[x]) ans+=x+g[i]-1-h[x]; } return ans; } }sa; inline void T_add(int &g,int l,int r,int x) { if(!g) g=++num; if(l==r) return; int mid=l+r>>1; if(x<=mid) T_add(ch[g][0],l,mid,x); else T_add(ch[g][1],mid+1,r,x); } inline void Merge(int u,int &v,int l,int r) { if(!u) return; if(!v) return v=u,void(); int mid=l+r>>1; Merge(ch[u][0],ch[v][0],l,mid); Merge(ch[u][1],ch[v][1],mid+1,r); } inline void dfs(int x) { L[x]=sz+1; if(sam.pos[x]) { ++sz; T_add(root[sz],1,n,sam.pos[x]); Merge(root[sz-1],root[sz],1,n); } int tot=to[x].size(); for1(1,tot,i) dfs(to[x][i-1]); R[x]=sz; } inline int query(int u,int v,int l,int r,int x) { if(v==u||l>x) return 0; if(l==r) return l; int ans=0; int mid=l+r>>1; ans=query(ch[u][1],ch[v][1],mid+1,r,x); if(ans) return ans; return query(ch[u][0],ch[v][0],l,mid,x); } int main () { //freopen("a.in","r",stdin); scanf("%s",s+1); n=strlen(s+1); sam.init(); for1(1,n,i) sam.extend_(s[i]-'a'),sam.pos[sam.las]=i; for1(2,sam.node_num,i) to[sam.fa[i]].push_back(i); bool vis=0; int q_size=read(); while (q_size--) { int l,r; scanf("%s%d%d",s+1,&l,&r); if(!(l==1&&r==n)&&!vis) vis=1,dfs(1); m=strlen(s+1); sa.build(); ll ans=0; int now=1; for1(1,m,i) { h[i]=max(h[i-1],i-1); while (sam.nxt[now][s[h[i]+1]-'a']) { int ha=sam.nxt[now][s[h[i]+1]-'a']; if(!(l==1&&r==n)&&query(root[L[ha]-1],root[R[ha]],1,n,r)-h[i]+i-1<l) break; ++h[i],now=ha; if(h[i]==m) break; } if(h[i]==m) { for1(i+1,m,j) h[j]=m; break; } ans+=m-h[i]; if(sam.mx[sam.fa[now]]==h[i]-i) now=sam.fa[now]; } printf("%lld\n",ans-sa.calc_()); } }