BZOJ 4480: [Jsoi2013]快乐的jyy(回文自动机)

Description

【故事背景】
JYY在JSOI有很多很多的好朋友,比如PUPPY,KFC还有PUPPUP。因为
有了这么多的好朋友,所以JYY每天都很快乐。某天,JYY发现好朋友之间关
系的好坏和名字有很大的关系,比如PUPPY和PUPPUP的关系就特别好,但是
和KFC的关系就很一般。JYY苦思冥想终于发现了其中的规律,现在JYY想知
道两个朋友之间关系的好坏,你能帮助JYY么?
【问题描述】
给定两个字符串A和B,表示JYY的两个朋友的名字。我们用A(i,j)表示A
字符串中从第i个字母到第j个字母所组成的子串。同样的,我们也可以定义B(x,y)。
JYY发现两个朋友关系的紧密程度,等于同时满足如下条件的四元组(i,j,x,y)
的个数:
1:1<=i<=j<=|A|
2:1<=x<=y<=|B|
3:A(i,j)=B(x,y)
4:A(i,j)是回文串
这里表示字符串A的长度。
JYY希望你帮助他计算出这两个朋友之间关系的紧密程度。
Input

数据包行两行由大写字母组成的字符串A和B
1≤|A|,|B|≤50000。

Output

包含一行一个整数,表示紧密程度,也就是满足要求的4元组个数


题解:
感觉回文自动机的题比AC自动机和SAM友好多了(

其实只需要知道A,B串中有多少对相同的回文串就好了,由于回文自动机上的结点代表的回文串都是唯一的,所以我们可以分别建回文自动机,然后求出每个回文串的出现次数,同时dfs奇根和偶根,出现次数相乘然后累加即可


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
#define pii pair<int,int>
#define mp(a,b) make_pair(a,b)
const int MAXN = 5e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
char s[MAXN],t[MAXN];
struct PAM{
    int nxt[MAXN][26],fail[MAXN],cnt[MAXN],len[MAXN];
    int tot,last;
    inline void Init(){
        len[0]=0,len[1]=-1;
        fail[0]=fail[1]=1;
        last=0,tot=1;
    }
    inline void Insert(int x,int en,char *s){
        int rt=last;
        while(s[en]!=s[en-len[rt]-1]) rt=fail[rt];
        if(!nxt[rt][x]){
            int tmp=fail[rt]; ++tot;
            while(s[en]!=s[en-len[tmp]-1]) tmp=fail[tmp];
            fail[tot]=nxt[tmp][x];
            nxt[rt][x]=tot;
            len[tot]=len[rt]+2;
        }
        last = nxt[rt][x]; cnt[last]++;
    }
    inline void solve(){
        for(int i=tot;i;i--) cnt[fail[i]] += cnt[i];
    }
}p1,p2;
LL ans;
void dfs(int u,int v){
    if(u>1 && v>1) ans+=1LL*p1.cnt[u]*p2.cnt[v];
    for(int i=0;i<26;i++)
        if(p1.nxt[u][i] && p2.nxt[v][i])
            dfs(p1.nxt[u][i],p2.nxt[v][i]);
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    p1.Init(); p2.Init();
    cin>>(s+1)>>(t+1);
    int n=strlen(s+1),m=strlen(t+1);
    for(int i=1;i<=n;i++) p1.Insert(s[i]-'A',i,s);
    for(int i=1;i<=m;i++) p2.Insert(t[i]-'A',i,t);
    p1.solve(); p2.solve();
    dfs(1,1); dfs(0,0); cout<<ans<<'\n';
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值