题目
一个长度为n的字符串S,令
Ti
表示它从第i个字符开始的后缀.求
∑1≤i<j≤nlen(Ti)+len(Tj)−2∗lcp(Ti,Tj)
其中len(a)表示字符串a的长度,lcp(a,b)表示字符串a和字符串b的最长公共前缀.
输入
一行,一个字符串S
输出
一行,一个整数,表示所求值
样例输入
cacao
样例输出
54
注意
2≤N≤500000
,S由小写英文字母组成
分析
这道题需要我们记住一个结论 ! ! !
!反串建后缀自动机的parent树是原串的后缀树!
对于这道题,我们不难发现,原串后缀树上两节点的最近公共祖先的层数就是两子串的lcp,我们可以从叶子结点开始往父亲节点走,根据题目要求就是求出所有公共前缀,我们可以边记录边更新,具体操作可以参考代码第63,64行!
完整代码
#include<bits/stdc++.h>
#define maxn 500010
#define maxt 1000010
using namespace std;
int n;
long long ans=
0;
int sum[maxt],tmp[maxt];
long long total[maxt];
char ch[maxn];
struct SAM
{
int root,last,tot;
int r[maxt],fa[maxt],son[maxt][
26],maxl[maxt];
int addnode(
int x) {
return maxl[++tot]=x,tot; }
void init() { root=last=tot=
1; }
void add(
int pos)
{
int x=ch[pos]-
'a',p=last,np=addnode(maxl[p]+
1);
last=np,r[np]=total[np]=
1;
for( ; p&&!son[p][x] ; p=fa[p] ) son[p][x]=np;
if(!p) fa[np]=root;
else
{
int q=son[p][x];
if(maxl[q]==maxl[p]+
1) fa[np]=q;
else
{
int nq=addnode(maxl[p]+
1);
memcpy(son[nq],son[q],
sizeof(son[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for( ; son[p][x]==q ; p=fa[p] ) son[p][x]=nq;
}
}
}
void Tsort()
{
for(
int i=
1;i<=tot;++i) sum[maxl[i]]++;
for(
int i=
1;i<=n;++i) sum[i]+=sum[i-
1];
for(
int i=
1;i<=tot;++i) tmp[sum[maxl[i]]--]=i;
}
void treeDP()
{
for(
int i=tot;i;--i) r[fa[tmp[i]]]+=r[tmp[i]];
for(
int i=tot;i;--i)
{
int x=tmp[i];
ans+=
1ll*total[fa[x]]*r[x]*maxl[fa[x]];
total[fa[x]]+=r[x];
}
}
void build()
{
init();
scanf(
"%s",ch+
1);
n=
strlen(ch+
1);
for(
int i=n;i;--i) add(i);
Tsort();
treeDP();
}
} sam ;
int main()
{
#ifdef DEBUG
freopen(
"in.txt",
"r",stdin);
freopen(
"out.txt",
"w",stdout);
#endif
sam.build();
printf(
"%lld\n",
1ll*(n-
1)*(n+
1)*n/
2-ans*
2);
return 0;
}
转载请注明原文地址: https://ju.6miu.com/read-24300.html