问题背景
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;
}