题解 P4070 [SDOI2016]生成魔咒
后缀树题解
Solution
在建后缀树的 \(\mathsf{Ukkonen}\) 算法中,是在线插入字符的,所以能很好地解决此问题。
我们知道,一个字符串的本质不同子串就是其后缀树上所有边长之和,那么只要在每次插入时考虑有哪些边会变化即可。
因为后缀树是压缩 \(\mathsf{Trie}\),所以每次插入只有叶子节点的边会改变。
- 当插入成功时,会有分裂节点产生,此时会多一条边长为 \(1\) 的边。
- 当插入失败时,若要新建节点,也会多一条边长为 \(1\) 的边。
那么在这两处更新答案即可。
Code
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
const int N=2e5+5,inf=1e8;
long long cnt,ans;
struct Suffix_Tree{
int n,tot,now,rem,link[N],s[N],len[N],st[N];
__gnu_pbds::gp_hash_table<int,int>ch[N];
Suffix_Tree():n(0),tot(1),now(1),rem(0){len[0]=inf;}
int node(int p,int l){link[++tot]=1;st[tot]=p;len[tot]=l;return tot;}
void ins(int x){
s[++n]=x;rem++;
for(int lst=1;rem;){
while(rem>len[ch[now][s[n-rem+1]]])
rem-=len[now=ch[now][s[n-rem+1]]];
int &v=ch[now][s[n-rem+1]],c=s[st[v]+rem-1];
if(!v||x==c){//插入失败
link[lst]=now;lst=now;
if(!v)v=node(n,inf),cnt++;//要新建节点
else break;
}else{//插入成功
int u=node(st[v],rem-1);
ch[u][c]=v;ch[u][x]=node(n,inf);cnt++;//分裂节点
st[v]+=rem-1;len[v]-=rem-1;
link[lst]=v=u;lst=u;
}if(now==1)rem--;else now=link[now];
}
}
}T;
int n,x;
int main(){
for(scanf("%d",&n);n--;printf("%lld\n",ans+=cnt))
scanf("%d",&x),T.ins(x);
return 0;
}

浙公网安备 33010602011771号