【算法介绍】
线性求出每个点作为中心节点的,最长回文子串的半径
【例题】
1.hdu5340
【题意】
判断是否能成为3个非空回文子串
【分析】
manacher跑出p数组,然后考虑每个枚举从左和从右的两个回文串,并判断中间的串是否是回文串即可
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e4+5;
char s[maxn],a[maxn];
int t,n,p[maxn];
void manacher()
{
int mx=0,id;
for(int i=0;i<n;i++)
{
if(mx>i)
p[i]=min(p[id*2-i],mx-i);
else p[i]=1;
while(a[i+p[i]]==a[i-p[i]]) p[i]++;
if(p[i]+i>mx)
{
mx=p[i]+i;
id=i;
}
p[i]--;
}
}
int l[maxn],r[maxn],p1,p2;
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%s",s+1);
a[0]='#';
n=0;
int len=strlen(s+1);
for(int j=1;j<=len;j++) a[++n]=s[j],a[++n]='#';
a[++n]='\0';
manacher();
p1=p2=0;
for(int i=1;i<n-1;i++)
{
if(i-p[i]==0) l[++p1]=i;
if(i+p[i]==n-1) r[++p2]=i;
}
int flag=0;
for(int i=1;i<=p1;i++)
{
for(int j=p2;j>=1;j--)
{
int pl=l[i]+p[l[i]]+1,pr=r[j]-p[r[j]]-1;
if(pl>pr) break;
int mid=pl+pr>>1;
if(p[mid]>=mid-pl)
{
flag=1;
break;
}
}
if(flag) break;
}
if(flag) printf("Yes\n");
else printf("No\n");
}
return 0;
}
2.CF17E Palisection
【题意】
求重叠的回文子串对数
【分析】
考虑正难反解,我们算出所有的回文串个数cnt,cnt*(cnt-1)/2就是总共回文串的对数
然后利用manacher算出p数组,处理出来每个位置起始和结束的串有多少个,然后差分前缀和统计即可
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e6+5;
const int mod=51123987;
int n,l,p[maxn],le[maxn],ri[maxn];
char s[maxn],a[maxn];
void manacher()
{
int mx=0,id;
for(int i=0;i<l;i++)
{
if(mx>i) p[i]=min(p[id*2-i],mx-i);
else p[i]=1;
while(a[i+p[i]]==a[i-p[i]]) p[i]++;
if(i+p[i]>mx)
{
mx=i+p[i];
id=i;
}
}
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&n);
scanf("%s",s);
a[1]='#'; l=1;
int len=strlen(s);
for(int i=0;i<len;i++) a[++l]=s[i],a[++l]='#';
a[++l]='@';
manacher();
ll ans=0,tmp=0;
for(int i=1;i<=l;i++)
le[i-p[i]+1]++,le[i+1]--,ri[i+p[i]]--,ri[i]++,ans=(ans+p[i]/2)%mod;
ans=1LL*ans*(ans-1)/2%mod;
for(int i=1;i<=l;i++)
{
le[i]=(le[i]+le[i-1])%mod; ri[i]=(ri[i]+ri[i-1])%mod;
if(i%2==0) ans=(ans-1LL*tmp*le[i]%mod+mod)%mod,tmp=(tmp+ri[i])%mod;
}
printf("%lld",ans);
return 0;
}