@bzoj - 3238@ [Ahoi2013]差异


@description@

给定一个长度为 n 的字符串 S,令 Ti 表示它从第 i 个字符开始的后缀。求:

\[\sum_{1\le i < j \le n}((len(Ti) -lcp(Ti, Tj)+(len(Tj)-lcp(Ti, Tj)) \]

其中 lcp 是最长公共前缀。

input
一个长度为 n 的字符串S。2 <= n <= 500000,S由小写英文字母组成。
output
一个整数,表示所求值。

sample input
cacao
sample output
54

@solution@

那个式子我们可以分两部分求解:len 和 lcp。

len 部分:每一个后缀的 len 都会统计 n-1 次。所以它对答案的贡献为 \((1+2+...+n)*(n-1)\)。把等差数列的求和公式代进去:\(\frac{(n-1)*n*(n+1)}{2}\)

lcp 部分:我们把原串翻转,则原串中的后缀对应新串中的前缀,我们要求解原串中的最长公共前缀就是新串中的最长公共后缀。
一个结点的 fa 所表示的结点一定是这个结点的后缀。所以我们最长公共后缀所表示的结点一定是该结点的某个祖先。
那么两个结点的 lca 就能表示它们的最长公共后缀。因此我们作一个简单的树形 dp 统计有多少对点以根为 lca 即可。

实际上,后缀自动机在翻转的串上建出来的 parent 树,就是原串中的后缀树。

所以后缀树完完全全没什么用啊喂。

@accepted code@

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 500000;
vector<int>G[2*MAXN + 5];
struct sam{
	sam *ch[26], *fa; int mx;
	int tag;
}pl[2*MAXN + 5], *root, *tcnt, *lst;
void init() {
	root = tcnt = lst = &pl[0];
}
sam *newnode(int x) {
	tcnt++; tcnt->tag = x;
	return tcnt;
}
void clone(sam *x, sam *y) {
	for(int i=0;i<26;i++)
		x->ch[i] = y->ch[i];
	x->fa = y->fa;
}
void sam_extend(int x) {
	sam *cur = newnode(1), *p = lst;
	cur->mx = lst->mx + 1; lst = cur;
	while( p && !p->ch[x] )
		p->ch[x] = cur, p = p->fa;
	if( !p )
		cur->fa = root;
	else {
		sam *q = p->ch[x];
		if( p->mx + 1 == q->mx )
			cur->fa = q;
		else {
			sam *nq = newnode(0);
			nq->mx = p->mx + 1;
			clone(nq, q);
			q->fa = cur->fa = nq;
			while( p && p->ch[x] == q )
				p->ch[x] = nq, p = p->fa;
		}
	}
}
int siz[2*MAXN + 5];
char s[MAXN + 5]; ll ans;
void dfs(int rt) {
	siz[rt] = pl[rt].tag;
	for(int i=0;i<G[rt].size();i++) {
		int to = G[rt][i]; dfs(to);
		ans -= 2LL*siz[rt]*siz[to]*pl[rt].mx;
		siz[rt] += siz[to];
	}
}
int main() {
	init(); scanf("%s", s);
	int lens = strlen(s);
	ans = 1LL*(lens-1)*lens*(lens+1)/2;
	for(int i=lens-1;i>=0;i--)
		sam_extend(s[i] - 'a');
	for(int i=1;i<=tcnt-pl;i++) {
		G[pl[i].fa-pl].push_back(i);
	}	
	dfs(0);
	printf("%lld\n", ans);
}

@details@

所以真的想问问大家,后缀树既然可以通过后缀自动机构造出来,时间复杂度空间复杂度也不会更优秀(因为你不可能超过线性复杂度嘛)。

那么后缀树到底用处在哪里?

posted @ 2019-01-11 17:44  Tiw_Air_OAO  阅读(188)  评论(0编辑  收藏  举报