题意:
求一个字符串中相交回文串的对数。
题解:
先求字符串中回文串的总对数,再减去不相交回文串的对数。
用Manacher求出回文半径,求和就是回文串的个数,平方除2就是回文串总对数。
下面求不相交回文串的对数。
设s[i]表示以i为结束的回文串的个数,s1[i]表示以第j(1<=j<=i)为结束的回文子串的个数和。设当前处理到以第i位为中心的回文子串与左侧回文子串的不相交对数,即
为了快速求得上式,再求前缀和即可。
代码:
#include<bits/stdc++.h>
#define N 2000010
#define LL long long
using namespace std;
char s[N],ss[N<<1];
int r[N<<1],len;LL s1[N];
int Init()
{
int len = strlen(s);
ss[0] = '$';ss[1] = '#';
int j = 2;
for (int i = 0; i < len; i++)ss[j++] = s[i],ss[j++] = '#';
ss[j] = '\0';
return j;
}
void Manacher()
{
len=Init();
int p,mx=0;
for (int i = 1; i < len; i++)
{
if (i<mx) r[i]=min(r[2*p-i],mx-i);else r[i] = 1;
while (ss[i-r[i]]==ss[i+r[i]]) r[i]++;
if (mx<i+r[i])p=i,mx=i+r[i];
}
}
int main()
{
int n;
while (~scanf("%d%s",&n,s))
{
Manacher();
LL tot=0;
memset(s1,0,sizeof s1);
for (int i=1;i<=n*2;i++)
{
s1[i/2+(i&1)]++;
s1[i/2+(i&1)+r[i]/2]--;
tot=(tot+r[i]/2)%51123987;
}
tot=tot*(tot-1)/2%51123987;
for (int i=2;i<=n;i++) s1[i]+=s1[i-1];
for (int i=2;i<=n;i++) s1[i]+=s1[i-1];
for (int i=2;i<=n;i++) s1[i]+=s1[i-1];
LL ans=0;
for (int i=2;i<=n*2;i++) ans+=s1[i/2-1]-s1[i/2-r[i]/2-1],ans%=51123987;
printf("%I64d\n",(tot-ans+51123987)%51123987);
}
}