题解 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;
}
posted @ 2021-09-05 23:57  ADay526  阅读(44)  评论(0)    收藏  举报