P4248 [AHOI2013]差异
题目大意
长度为 $n$的字符串$S$,令$T_i$表示它从第$i$个字符开始的后缀,求$\sum\limits_{1\leqslant i<j\leqslant n}^{} len(T_i)+len(T_j)+2×lcp(T_i+T_j)$
其中,$lcp(a,b)$表示$a$和$b$的最长公共前缀
Ps:$T_i$表示第i个字符开始的后缀,注意是开始,$juruo$看了半天样例都看不懂$emmm$
前半部分显然能化简成$\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^ni+j$ $=$ $(n-1)$ $×$ $\sum\limits_{i=1}^ni$ $=$$\frac{(n-1)×n×(n+1)}{2}$
至于后半部分:
后缀的最长公共前缀等价于反串前缀的最长公共后缀
构造出反串的$parent$树,根据父子关系dp出每个后缀的累加次数
My complete code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL maxn=2000000;
LL Len,last=1,nod=1,ans;
LL len[maxn],fail[maxn],son[maxn][26],val[maxn],t[maxn],a[maxn];
char s[maxn];
inline void Insert(LL c){
LL p=last,np=++nod;
last=np;
len[np]=len[p]+1;
val[np]=1;
while(p&&!son[p][c]){
son[p][c]=np;
p=fail[p];
}
if(!p)
fail[np]=1;
else{
LL q=son[p][c];
if(len[q]==len[p]+1)
fail[np]=q;
else{
LL nq=++nod;
len[nq]=len[p]+1;
memcpy(son[nq],son[q],sizeof(son[q]));
fail[nq]=fail[q];
fail[q]=fail[np]=nq;
while(p&&son[p][c]==q){
son[p][c]=nq;
p=fail[p];
}
}
}
}
int main(){
scanf(" %s",s+1);
Len=strlen(s+1);
for(LL i=Len;i>=1;--i)
Insert(s[i]-'a');
for(LL i=1;i<=nod;++i)
++t[len[i]];
for(LL i=1;i<=nod;++i)
t[i]+=t[i-1];
for(LL i=1;i<=nod;++i)
a[t[len[i]]--]=i;
for(LL i=nod;i>=1;--i)
val[fail[a[i]]]+=val[a[i]];
ans=(Len+1)*(Len-1)*Len/2;
for(int i=2;i<=nod;++i)
ans-=(val[i]-1)*val[i]*(len[i]-len[fail[i]]);
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号