bzoj 3238: [Ahoi2013]差异 (后缀自动机+树形dp)

3238: [Ahoi2013]差异

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 2268   Solved: 1031
[ Submit][ Status][ Discuss]

Description

Input

一行,一个字符串S

Output

 

一行,一个整数,表示所求值

Sample Input

cacao

Sample Output


54

HINT



2<=N<=500000,S由小写英文字母组成

Source

[ Submit][ Status][ Discuss]


题解:后缀自动机

这道题需要知道后缀自动机的一个性质,两个串的最长公共后缀,位于这两个串对应状态在parent树的最近公共祖先上。

分析一下题目中的式子,发现sigma len(Ti)+len(Tj)可以直接进行求解。关键是如何求解lcp ,因为上面的性质求得是最长公共后缀,所以我们将原串倒置,建立后缀自动机,得到的最长公共后缀就是原串的最长公共前缀。

知道这个后,问题就变成了,如何求解某个状态是多少对节点的后缀,这个可以用树形dp的思想,从下往上依次更新。

#include
#include
#include
#include
#include
#define N 1000003
#define LL long long
using namespace std;
int n,m,cnt,root,last,np,nq,p,q;
int ch[N][30],fa[N],l[N],a[N],v[N],r[N],pos[N];
LL sum[N];
char s[N];
void extend(int x)
{
	int c=a[x];
	p=last; np=++cnt; last=np;
	l[np]=x;
	for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
	if (!p) fa[np]=root;
	else {
		q=ch[p][c];
		if (l[q]==l[p]+1) fa[np]=q;
		else {
			nq=++cnt; l[nq]=l[p]+1;
			memcpy(ch[nq],ch[q],sizeof ch[nq]);
			fa[nq]=fa[q];
			fa[q]=fa[np]=nq;
			for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
		}
	}
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1);
	last=root=++cnt;
	for (int i=1;i<=n;i++) a[n-i+1]=s[i]-'a';
	for (int i=1;i<=n;i++) extend(i);
	for (int i=1;i<=cnt;i++) v[l[i]]++;
	for (int i=1;i<=n;i++) v[i]+=v[i-1];
	for (int i=1;i<=cnt;i++) pos[v[l[i]]--]=i;
	p=1; 
	for (int i=1;i<=n;i++) {
		p=ch[p][a[i]]; r[p]++; sum[p]++;
	}
	for (int i=cnt;i;i--){
		int t=pos[i];
		r[fa[t]]+=r[t];
	}
	/*for (int i=1;i<=cnt;i++) cout<=1;i--){
		int t=pos[i];
		ans+=(LL)l[fa[t]]*(LL)r[t]*(LL)sum[fa[t]];
		sum[fa[t]]+=(LL)r[t];
	}
//	cout<



你可能感兴趣的:(动态规划,字符串处理,后缀自动机)