bzoj 3238: [Ahoi2013]差异 后缀树

       学习了一下后缀自动机转后缀树的方法。虽然可能这道题目后缀数组也可以做。

       考虑后缀自动机的parent,它对应的是比x的right集合略大一点的那个节点;对应到后缀树上,可以发现在缩边后对应的就是y的父亲。

       但是后缀自动机求得实际上是所有前缀倒过来后的后缀树;因此把原串反过来跑sam,然后x连向fa[x]就是后缀树了;每个点的len实际上就是这个节点的深度。

       然后考虑这道题目,本质就是求两两的lcp的长度,那就对每个后缀树上的节点求一下就好了。

AC代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;

int n; char s[N];
struct sam{
	int cnt,last,ch[N][26],len[N],fa[N],fst[N],nxt[N],sz[N];
	ll ans;
	sam(){ cnt=last=1; }
	void ins(int c){
		int p=last,np=last=++cnt; len[np]=len[p]+1; sz[cnt]=1;
		for (; p && !ch[p][c]; p=fa[p]) ch[p][c]=np;
		if (!p) fa[np]=1; else{
			int q=ch[p][c];
			if (len[p]+1==len[q]) fa[np]=q; else{
				int nq=++cnt; len[nq]=len[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				fa[nq]=fa[q]; fa[np]=fa[q]=nq;
				for (; p && ch[p][c]==q; p=fa[p]) ch[p][c]=nq;
			}
		}
	}
	void dfs(int x){
		int y;
		for (y=fst[x]; y; y=nxt[y]){
			dfs(y);
			ans+=(ll)len[x]*sz[x]*sz[y]; sz[x]+=sz[y];
		}
	}
	void solve(){
		int i;
		for (i=2; i<=cnt; i++){
			nxt[i]=fst[fa[i]]; fst[fa[i]]=i;
		}
		dfs(1);
		printf("%lld\n",((ll)n*(n-1)*(n+1)>>1)-(ans<<1));
	}
}sam;
int main(){
	scanf("%s",s+1); n=strlen(s+1);
	int i;
	for (i=n; i; i--) sam.ins(s[i]-'a');
	sam.solve();
	return 0;
}


by lych

2017.3.16

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值