[BZOJ4566][HAOI2016]找相同字符 后缀自动机

题目要求的就是B的每个字串在A中的出现次数之和。
我们考虑先建出A串的SAM,每个点所代表的串的个数就是 |Righti|(MaxiMaxfai) 。我们可以发现经过一个点的时候,它在parent树上所有祖先代表的串出现次数也都加一,所以我们设 Sumi 为它和它祖先所代表串个数之和。
那么把B放在SAM上面跑,每经过一个点 x ,对答案的贡献为Sumfax+|Rightx|(disMaxfax) dis 是B串走到当前节点可拓展的最长长度。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=400010;
char s[maxn];
struct sam
{
    int cnt,last,a[maxn][26],mx[maxn],fa[maxn],buc[maxn],ord[maxn];
    ll ri[maxn],sum[maxn];
    sam(){last=cnt=1;for(int i=0;i<=25;i++)a[0][i]=1;mx[0]=-1;}
    void extend(int c)
    {
        int p=last,np=last=++cnt;mx[np]=mx[p]+1;ri[np]=1;
        for(;p&&!a[p][c];p=fa[p]) a[p][c]=np;
        int q=a[p][c];
        if(mx[q]==mx[p]+1) {fa[np]=q;return;}
        int nq=++cnt;mx[nq]=mx[p]+1;
        for(int ic=0;ic<26;ic++)
            a[nq][ic]=a[q][ic];
        fa[nq]=fa[q];
        fa[q]=fa[np]=nq;
        for(;p&&a[p][c]==q;p=fa[p]) a[p][c]=nq; 
    }
    void pre()
    {
        for(int i=1;i<=cnt;i++) buc[mx[i]]++;
        for(int i=1;i<=cnt;i++) buc[i]+=buc[i-1];
        for(int i=cnt;i;i--) ord[buc[mx[i]]--]=i;
        for(int i=cnt;i;i--) ri[fa[ord[i]]]+=ri[ord[i]];
        for(int i=2;i<=cnt;i++) sum[ord[i]]=sum[fa[ord[i]]]+ri[ord[i]]*(mx[ord[i]]-mx[fa[ord[i]]]);
    }
    ll run(int l)
    {
        ll re=0;
        for(int i=1,p=1,dis=0;i<=l;i++)
        {
            int c=s[i]-'a';
            if(a[p][c]) dis++;
            else
            {   
                while(p&&!a[p][c]) p=fa[p];
                dis=mx[p]+1;
            }
            p=a[p][c];
            re+=sum[fa[p]]+ri[p]*(dis-mx[fa[p]]);
        }

        return re;
    }
}Tsam;
int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
        Tsam.extend(s[i]-'a');
    Tsam.pre();
    scanf("%s",s+1);
    printf("%lld",Tsam.run(strlen(s+1)));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值