P4248 [AHOI2013]差异

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;
}

  

posted @ 2018-12-13 19:11  y2823774827y  阅读(266)  评论(0编辑  收藏  举报