P4248-[AHOI2013]差异【SAM or SA】

正题

题目链接:https://www.luogu.com.cn/problem/P4248


题目大意

T i T_i 表示后缀 i n i\sim n
一个字符串求
i = 1 n j = i n l e n ( T i ) + l e n ( T j ) 2 l c p ( T i , T j ) \sum_{i=1}^n\sum_{j=i}^nlen(T_i)+len(T_j)-2*lcp(T_i,T_j)


解题思路

有两种做法,这里标程用的是 S A M SAM

S A M SAM 做法

这个式子和树上路径长度很像,顺着这个思路,我们可以想到 S A M SAM 上的 P a r e n t Parent 树。

两个后缀的 l c p lcp 就是 P a r e n t Parent 树上的 L C A LCA 的节点代表的字符串,我们让边长为 l e n x l e n f a i l x len_x-len_{fail_x} ,然后答案就变为了树上的每条路径长度和。

那一条边的贡献就是 ( n u m s i z x ) s i z x l e n (num-siz_x)*siz_x*len
统计即可,时间复杂度 O ( n ) O(n)

S A SA 做法

我们先计算定值 i = 1 n j = i n l e n ( T i ) + l e n ( T j ) \sum_{i=1}^n\sum_{j=i}^nlen(T_i)+len(T_j)
这里的定值就是 n ( n 1 ) ( n + 1 ) 2 \frac{n*(n-1)*(n+1)}{2}

然后我们考虑如何计算 L C P LCP 的和
h e i g h t i = L C P ( i , i 1 ) height_i=LCP(i,i-1)
我们有 L C P ( i , j ) = m i n { h e i g h t k } ( i < k j ) LCP(i,j)=min\{height_k\}(i<k\leq j)
所以这里的答案就变为了 i = 2 n j = i n m i n { h e i g h t k } ( i k j ) \sum_{i=2}^{n}\sum_{j=i}^nmin\{height_k\}(i\leq k\leq j)
我们每个 h e i g h t i height_i 的贡献分开统计即可


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e6+10;
struct node{
	ll to,next,w;
}a[N];
ll n,tot,ls[N],ans,num,siz[N];
ll next[N][26],len[N],fail[N],cnt;
char s[N];
void New_Point(ll x,ll v){
	next[x][v]=++cnt;
	len[cnt]=len[x]+1;
}
void addl(ll x,ll y,ll w){
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;
	a[tot].w=w;
}
void Make_SAM(){
	ll now;
	now=cnt=1;
	for(ll i=1;i<=n;i++){
		ll val=s[i]-'a';
		New_Point(now,val);
		ll x=now,y;now=cnt;
		for(y=fail[x];y;y=fail[y])
			if(!next[y][val])
				next[y][val]=now;
			else{
				if(len[y]+1==len[next[y][val]])
					fail[now]=next[y][val];
				else{
					ll z=next[y][val];
					New_Point(y,val);
					fail[cnt]=fail[z];
					fail[z]=fail[now]=cnt;
					for(ll i=0;i<26;i++)
						next[cnt][i]=next[z][i];
					for(ll j=y;j;j=fail[j])
						if(next[j][val]==z)
							next[j][val]=cnt;
				}
				break;
			}
		siz[now]=1;
		if(!y) fail[now]=1;
	}
	for(ll i=2;i<=cnt;i++){
		num+=siz[i];
		addl(fail[i],i,len[i]-len[fail[i]]);
	}
}
void dfs(ll x){
	for(ll i=ls[x];i;i=a[i].next){
		ll y=a[i].to;dfs(y);
		ans+=siz[y]*(num-siz[y])*a[i].w;
		siz[x]+=siz[y];
	}
} 
int main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	Make_SAM();
	dfs(1);
	printf("%lld",ans);
}
发布了867 篇原创文章 · 获赞 55 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104087931
sa