您的位置:首页 > 其它

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

2017-12-26 18:44 435 查看
题目要求的就是B的每个字串在A中的出现次数之和。

我们考虑先建出A串的SAM,每个点所代表的串的个数就是|Righti|∗(Maxi−Maxfai)。我们可以发现经过一个点的时候,它在parent树上所有祖先代表的串出现次数也都加一,所以我们设Sumi为它和它祖先所代表串个数之和。

那么把B放在SAM上面跑,每经过一个点x,对答案的贡献为Sumfax+|Rightx|∗(dis−Maxfax),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;
100f3
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: