生活随笔
收集整理的這篇文章主要介紹了
[BZOJ4566][HAOI2016]找相同字符 后缀自动机
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
題目要求的就是B的每個字串在A中的出現(xiàn)次數(shù)之和。
我們考慮先建出A串的SAM,每個點所代表的串的個數(shù)就是 |Righti|?(Maxi?Maxfai) 。我們可以發(fā)現(xiàn)經(jīng)過一個點的時候,它在parent樹上所有祖先代表的串出現(xiàn)次數(shù)也都加一,所以我們設(shè) Sumi 為它和它祖先所代表串個數(shù)之和。
那么把B放在SAM上面跑,每經(jīng)過一個點 x ,對答案的貢獻為Sumfax+|Rightx|?(dis?Maxfax), dis 是B串走到當前節(jié)點可拓展的最長長度。
代碼:
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;
}
總結(jié)
以上是生活随笔為你收集整理的[BZOJ4566][HAOI2016]找相同字符 后缀自动机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。