[AHOI2013] 差异 题解

后缀自动机维护子串公共后缀方便一点,所以直接倒序插入字符串即可。

我们给所有前缀打上标记,然后跑树形 \(dp\),设 \(sum_i\) 表示第 \(i\) 个点的子树内有多少个前缀,\(ans\) 统计 \(\sum \text{LCP}(T_i,T_j)\),则有:

\[ans=\sum\limits_{i=1}^{id}\sum\limits_{j\in ison} {sum}_j({sum}_i-{sum}_j) \]

简化求解式发现就是 \(\dfrac{n(n-1)(n+1)}{2}-ans\),时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
struct SAM{
	int id,sm[N],ln[N];
	int tl,tr[N][26],pr[N];
	vector<int>g[N];ll ans;
	SAM(){pr[0]=-1;}
	void cpy(int x,int y){
		for(int i=0;i<26;i++)
			tr[x][i]=tr[y][i];
		pr[x]=pr[y];ln[x]=ln[y];
	}void add(int x){
		ln[++id]=ln[tl]+1;
		int p=tl;tl=id;
		while(~p&&!tr[p][x])
			tr[p][x]=id,p=pr[p];
		if(p<0){
			sm[tl]++;
			return;
		}int u=p,v=tr[p][x];
		if(ln[u]+1==ln[v]){
			pr[id]=v;
			sm[tl]++;
			return;
		}cpy(++id,v);
		ln[id]=ln[u]+1;
		pr[v]=pr[id-1]=id;
		while(~p&&tr[p][x]==v)
			tr[p][x]=id,p=pr[p];
		sm[tl]++;
	}void jb(){
		for(int i=1;i<=id;i++)
			g[pr[i]].push_back(i);
	}void dfs(int x){
		ll sum=sm[x];
		for(int i=0;i<g[x].size();i++){
			int y=g[x][i];dfs(y);
			ans+=sum*sm[y]*ln[x];
			sum+=sm[y];
		}sm[x]=sum;
	}
}sam;char s[N];int n;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>s;n=strlen(s);
	for(int i=n-1;~i;i--)
		sam.add(s[i]-'a');
	sam.jb();sam.dfs(0);
	cout<<(ll)(n-1)*n*(n+1)/2-sam.ans*2;
	return 0;
}//man!what can I say!
posted @   长安一片月_22  阅读(8)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示