問題背景
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
鏈接
解析
在這道題中,我們需要統計第二個字符串中的回文串在第一個串中的出現情況。這種問題,我們可以用回文自動機來實現。對第一個串建立回文自動機,然后在建好的fail樹上做一次子樹和統計每一種回文串出現的次數,記為\(sum_i\)。然后用第二個字符串上的每一個位置在回文自動機上匹配,記錄每個點的匹配次數,然后同樣在做一次子樹和記錄每個回文串被匹配的次數,記為\(cnt_i\)。最后答案就是:
\[\sum_{i=1}^n sum_i\times cnt_i \]
代碼
#include <iostream>
#include <cstdio>
#include <cstring>
#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;
}