[JSOI2013]快乐的 JYY

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;
}
posted @ 2021-09-25 18:33  Evitagen  阅读(32)  评论(0编辑  收藏  举报