[BZOJ4480] JSOI2013 快乐的jyy

问题背景

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希望你帮助他计算出这两个朋友之间关系的紧密程度。

输入格式

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

输出格式

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

样例输入

PUPPY
PUPPUP

样例输出

17

链接

BZOJ

解析

在这道题中,我们需要统计第二个字符串中的回文串在第一个串中的出现情况。这种问题,我们可以用回文自动机来实现。对第一个串建立回文自动机,然后在建好的fail树上做一次子树和统计每一种回文串出现的次数,记为\(sum_i\)。然后用第二个字符串上的每一个位置在回文自动机上匹配,记录每个点的匹配次数,然后同样在做一次子树和记录每个回文串被匹配的次数,记为\(cnt_i\)。最后答案就是:
\[ \sum_{i=1}^n sum_i\times cnt_i \]

代码

#include 
#include 
#include 
#define N 50002
using namespace std;
struct PAM{
    int son[26],fail,len,sum,cnt;
}t[N];
char a[N],b[N];
int n,m,i,num,last;
void insert(int p,int x)
{
    int cur=last;
    while(a[p-1-t[cur].len]!=a[p]) cur=t[cur].fail;
    if(!t[cur].son[x]){
        num++;
        int tmp=t[cur].fail;
        while(a[p-1-t[tmp].len]!=a[p]) tmp=t[tmp].fail;
        t[num].len=t[cur].len+2;
        t[num].fail=t[tmp].son[x];
        t[cur].son[x]=num;
    }
    t[t[cur].son[x]].sum++;
    last=t[cur].son[x];
}
void find(int p,int x)
{
    int cur=last;
    while(cur!=1&&(b[p-1-t[cur].len]!=b[p]||!t[cur].son[x])) cur=t[cur].fail;
    if(b[p-1-t[cur].len]==b[p]&&t[cur].son[x]) last=t[cur].son[x],t[last].cnt++;
    else last=1;
}
int main()
{
    scanf("%s%s",a+1,b+1);
    n=strlen(a+1);m=strlen(b+1);
    num=last=1;
    t[0].fail=t[1].fail=1;
    t[1].len=-1;
    for(i=1;i<=n;i++) insert(i,a[i]-'A');
    for(i=num;i>=1;i--) t[t[i].fail].sum+=t[i].sum;
    last=0;
    for(i=1;i<=m;i++) find(i,b[i]-'A');
    for(i=num;i>=1;i--) t[t[i].fail].cnt+=t[i].cnt;
    long long ans=0;
    for(i=2;i<=num;i++) ans+=1LL*t[i].sum*t[i].cnt;
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:([BZOJ4480] JSOI2013 快乐的jyy)