bzoj3238 [Ahoi2013]差异

Description


定义lcp(a,b)为字符串a、b的最长公共前缀,len(a)为字符串a的长度。对于长度位n的字符串S定义Ti为S从第i位开始的后缀,求

1<=i<j<=nlen(Ti)+len(Tj)len(lcp(Ti,Tj)) ∑ 1 <= i < j <= n l e n ( T i ) + l e n ( T j ) − l e n ( l c p ( T i , T j ) )

Solution


前面两项挺好求的,问题是后面的lcp要怎么搞
由于我太弱了不会后缀数组,因此考虑把S倒过来建SAM,得出的parent树上两节点的lca即为两字符串的lcp(反向前缀树),树形dp统计每一个节点单独的贡献即可

记得开LL

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define copy(x,t) memcpy(x,t,sizeof(x))

typedef long long LL;
const int N=1000205;
const int E=1000205;
const int L=500005;

struct edge {int x,y,next;} e[E];
int ls[N],edCnt;
int size[N];
int rec[N][26],len[N],fa[N];
int last,cnt;
LL ans;
char str[L];

void addEdge(int x,int y) {
    e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
}

void insert(int ch) {
    int p,q,np,nq;
    p=last; last=np=++cnt; len[np]=len[p]+1;
    size[np]=1;
    while (p&&!rec[p][ch]) {
        rec[p][ch]=np;
        p=fa[p];
    }
    if (!p) fa[np]=1;
    else {
        q=rec[p][ch];
        if (len[p]+1==len[q]) {
            fa[np]=q;
        } else {
            nq=++cnt; len[nq]=len[p]+1;
            copy(rec[nq],rec[q]);
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            while (p&&rec[p][ch]==q) {
                rec[p][ch]=nq;
                p=fa[p];
            }
        }
    }
}

void dfs(int now) {
    for (int i=ls[now];i;i=e[i].next) {
        dfs(e[i].y);
        size[now]+=size[e[i].y];
    }
    if (now==1) return ;
    len[now]-=len[fa[now]];
    ans-=(LL)size[now]*(LL)(size[now]-1)*(LL)len[now];
}

int main(void) {
    scanf("%s",str);
    int len=strlen(str);
    cnt=last=1;
    drp(i,len-1,0) {
        insert(str[i]-'a');
    }
    rep(i,2,cnt) addEdge(fa[i],i);
    ans=(LL)(len-1)*(LL)(len+1)*(LL)len/2;
    dfs(1);
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值