题意
给定一个长度为n的小写字母串。问你有多少对相交的回文子 串(包含也算相交) 。 输入格式
第一行是字符串长度n(1<=n<=2*10^6),第二行字符串 输出格式
相交的回文子串个数%51123987
Translated by liyifeng
解法
因为首先是回文串,所以考虑manacher,求出每个节点的f[i],然后考虑直接求相交的回文串对数量不是很好求,所以用总对数减去不相交的对数。
考虑计算不相交的对数,我们求出两个数组;l[i],r[i],表示以i为开头的回文串数量,和以i为结尾的回文串数量,然后,就是不相交的回文串对数。
如何计算l[],r[]?可以考虑用差分,对于一个回文中心i,(i-f[i],i]这一段都可以作为左端点出现,然后[i,i+f[i])这一段都可以作为右端点出现。
#include<bits/stdc++.h>
using namespace std;
const int mod=51123987;
const int maxn=4e6+5;
inline int read(){
char c=getchar();int t=0,f=1;
while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n;
int f[maxn],m,l[maxn],r[maxn];
char s[maxn];
int main(){
//freopen("17E.in","r",stdin);
//freopen("17E.out","w",stdout);
s[0]='$';m=1;s[1]='#';
n=read();
for(int i=1;i<=n;i++){
m++;
scanf("%c",&s[m]);
s[++m]='#';
}
int p=0,mx=0;
for(int i=1;i<=m;i++){
if(p+mx>i){
f[i]=min(p+mx-i,f[2*p-i]);
}
else{
f[i]=1;
}
while(s[i-f[i]]==s[i+f[i]]){
f[i]++;
}
if(i+f[i]>p+mx){
p=i;mx=f[i];
}
}
int ans=0;
for(int i=1;i<=m;i++){
l[i-f[i]+1]++;l[i+1]--;r[i]++;r[i+f[i]]--;
ans=(ans+f[i]/2)%mod;//这里除2的原因是在manacher中,会在原串中加新字符,这里消除了新字符的贡献
}
ans=1ll*ans*(ans-1)/2%mod;
for(int i=1,s=0;i<=m;i++){//做法是用总数减去不相交的字符串数量
l[i]+=l[i-1],r[i]+=r[i-1];
if(i%2==0){ans=(ans-1ll*s*l[i]%mod)%mod;s=(s+r[i])%mod;}
}
printf("%d\n",(ans+mod)%mod);
return 0;
}
来源:CSDN
作者:新笑雨
链接:https://blog.csdn.net/wmhtxdy/article/details/103983165