P4248 [AHOI2013]差异

思路

SAM
后缀自动机parent树的LCA就是两个子串的最长公共后缀
现在要求LCP
所以把字符串反转一下
然后每个点的贡献就是endpos的大小,dfs一遍求出贡献就可以了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int MAXN = 500500*2; 
int Nodecnt,trans[MAXN][26],suflink[MAXN],endpos[MAXN],maxlen[MAXN],fir[MAXN],nxt[MAXN],v[MAXN],cnt,ans,n;
char s[MAXN];
int sum(int l,int r){
    if(r<l)
        return 0;
    return (r+l)*(r-l+1)/2;    
}
void addedge(int ui,int vi){
    ++cnt;
    v[cnt]=vi;
    nxt[cnt]=fir[ui];
    fir[ui]=cnt;
}
int New_state(int _maxlen,int *_trans,int _suflink){
    int o=++Nodecnt;
    maxlen[o]=_maxlen;
    suflink[o]=_suflink;
    if(_trans){
        for(int i=0;i<26;i++)
            trans[o][i]=_trans[i];
    }
    return o;
}
int add_len(int u,int c){
    int z=New_state(maxlen[u]+1,NULL,0);
    endpos[z]=1;
    while(u&&(trans[u][c]==0)){
        trans[u][c]=z;
        u=suflink[u];
    }
    if(!u){
        suflink[z]=1;
        return z;
    }
    int v=trans[u][c];
    if(maxlen[u]+1==maxlen[v]){
        suflink[z]=v;
        return z;
    }
    int y=New_state(maxlen[u]+1,trans[v],suflink[v]);
    suflink[z]=suflink[v]=y;
    while(u&&trans[u][c]==v){
        trans[u][c]=y;
        u=suflink[u];
    }
    return z;
}
void dfs(int x){
    for(int i=fir[x];i;i=nxt[i]){
        dfs(v[i]);
        ans-=endpos[v[i]]*endpos[x]*maxlen[x]*2;
        endpos[x]+=endpos[v[i]];
    }
}
signed main(){
    scanf("%s",s+1);
    n=strlen(s+1); 
    reverse(s+1,s+n+1);
    Nodecnt=1;
    int last=1;
    for(int i=1;i<=n;i++)
        last=add_len(last,s[i]-'a');
    for(int i=1;i<=n;i++){
        int j=i+1;
        ans+=(n-i+1)*(n-j+1)+sum(1,n-j+1);
    }
    for(int i=2;i<=Nodecnt;i++)
        addedge(suflink[i],i);
    dfs(1);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-04-01 08:43  dreagonm  阅读(129)  评论(0编辑  收藏  举报