​P5685 [JSOI2013]快乐的 JYY​

题意:

给定两个字符串,求出两个串公共回文子串的个数。

分析:

还是先建立出 \(A\) 的后缀自动机。

考虑对于一个节点,我们应该记录它表示的回文串以它为后缀的回文串的总的出现次数。

统计以某个节点为后缀的,则总只会记录当前加入的 \(B\) 字符的相关的答案,不会统计到之前的。

像其他自动机一样,\(fail\) 数组可以看成是拓扑序,因此我们可以从上到下,算出来每个点对应的回文串在这个串里的个数有多少:

for(int i=cnt;i>=2;i--) if(fail[i]>1) num[fail[i]]+=num[i];


据此,我们可以求出 \(x\) 表示的节点的回文串的后缀出现次数的和

for(int i=2;i<=cnt;i++) num[i]+=num[fail[i]];


这样之后,我们跑 \(B\) 串进行匹配,到达的每个字符,统计 \(\sum num[now]\) ,即,匹配到的节点即可。

代码:

//P5685 [JSOI2013]快乐的 JYY
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
char s[N],b[N];
int son[N][26],len[N],cnt,fail[N],last,num[N];
int n,m;
long long ans;

void init(){
len[1]=-1; fail[1]=fail[0]=1; cnt=1; last=0;
}

int new_node(int length){ len[++cnt]=length; return cnt;}
int getfail(int x,int now){
while(s[now-len[x]-1]!=s[now]) x=fail[x];
return x;
}
void build_PAM(){
for(int i=1;s[i];i++){
int x=s[i]-'a',now=getfail(last,i);
if(!son[now][x]){
int newnode=new_node(len[now]+2);
fail[newnode]=son[getfail(fail[now],i)][x];
son[now][x]=newnode;
}
last=son[now][x];
num[last]++;
}
}

int main(){
scanf("%s%s",s+1,b+1);
n=strlen(s+1); m=strlen(b+1);
init();
build_PAM();
for(int i=cnt;i>=2;i--) if(fail[i]>1) num[fail[i]]+=num[i];
for(int i=2;i<=cnt;i++) num[i]+=num[fail[i]];
for(int i=1,now=0,x;i<=m;i++){//对于b上的回文串,我们处理
x=b[i]-'a';
//这里一定要记住有一个括号,要不然就会一直跑后面超时了
while(now!=1&&((b[i]!=b[i-len[now]-1])||!son[now][x])) now=fail[now];
if(son[now][x]) now=son[now][x];
else now=0;
ans+=num[now];
}
cout<<ans<<endl;
system("pause");
return 0;
}