题目大意:
给一个字符串,求存在多少“一个半回文串”。 一个半回文串:
(x个字母)+ (回文中心) +(x-1个字母)+(回文中心)+(x个字母)
例如: a b c b a b c
1<=n<=500000
题目思路:
分析什么时候会出现一个半回文串,只有两个回文串的回文半径互相相交时,就会出现一个半回文串。
回文半径明显可以用Manacher算法处理,所以问题转化成a[i]表示以i为中心的回文串的半径,如何求相交的对数。
此时容易想到对于每个i处理一个b[i]表示回文串最右端,用主席树处理回文串左端到回文串中心这段有多少个右端点大于i的就好了,但是有更方便、更快捷的数据结构来实现也就是树状数组。先预处理一个vector数组,存入以i为左端点的回文串的中心坐标,然后遍历数组,取出vector中的每一项,用树状数组在对应位置+1,并且查询 [ i+1 , i的回文右端点 ]的个数。
树状数组做法:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN = 5e5+5;
int b[MAXN],a[MAXN];
char s[MAXN],Ma[MAXN*2];
int Mp[MAXN*2],c[MAXN];
void add(int x,int k)
{
for(;x<=MAXN;x+=x&-x)
c[x]+=k;
}
ll ask(int x)
{
ll ans=0;
for(;x;x-=x&-x)
ans+=c[x];
return ans;
}
void Manacher(char s[],int len) /// Manacher
{
int l=0;
Ma[l++]='$';
Ma[l++]='#';
for(int i=0;i<len;i++){
Ma[l++]=s[i];
Ma[l++]='#';
}
Ma[l]=0;
int mx=0,id=0;
for(int i=0;i<l;i++){
Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
if(i+Mp[i]>mx){
mx=i+Mp[i];
id=i;
}
}
}
ll sum[MAXN*30];
int L[MAXN*30],R[MAXN*30],T[MAXN];
int cnt=0;
vector<int>v[MAXN];
int main()
{
ll ans;
int t;
scanf("%d",&t);
while(t--)
{
cnt=0;
scanf("%s",s);
memset(c,0,sizeof(c));
int len=strlen(s);
Manacher(s,len);
ans=0;
int n=len;
for(int i=1;i<=n;i++){
v[i].clear();
}
for(int i=1;i<2*len+2;i++){
if(Ma[i]!='#'){
int pos=i/2,now=(Mp[i]-1)/2;
b[pos]=min(pos+now,n);
a[pos]=max(1,pos-now);
v[a[pos]].push_back(pos);
}
}
for(int i=1;i<=n;i++){
int len=v[i].size();
for(int j=0;j<len;j++){
int now=v[i][j];
add(now,1);
}
ans+=ask(b[i])-ask(i);
}
printf("%lld\n",ans);
}
}
主席树做法:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN = 5e5+5;
int b[MAXN],a[MAXN];
char s[MAXN],Ma[MAXN*2];
int Mp[MAXN*2];
void Manacher(char s[],int len) /// Manacher
{
int l=0;
Ma[l++]='$';
Ma[l++]='#';
for(int i=0;i<len;i++){
Ma[l++]=s[i];
Ma[l++]='#';
}
Ma[l]=0;
int mx=0,id=0;
for(int i=0;i<l;i++){
Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
if(i+Mp[i]>mx){
mx=i+Mp[i];
id=i;
}
}
}
ll sum[MAXN*30];
int L[MAXN*30],R[MAXN*30],T[MAXN];
int cnt=0;
int update(int pre,int l,int r,int x) ///建主席树
{
int rt=++cnt;
L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;
int mid=(l+r)/2;
if(l<r){
if(x<=mid)L[rt]=update(L[pre],l,mid,x);
else R[rt]=update(R[pre],mid+1,r,x);
}
return rt;
}
ll query(int u,int v,int l,int r,int k) ///查询区间大于k的有多少个
{
ll ret=0;
int mid=(l+r)/2;
if(k<=l)return sum[v]-sum[u];
if(k<=mid)ret += query(L[u],L[v],l,mid,k);
ret+=query(R[u],R[v],mid+1,r,k);
return ret;
}
int main()
{
ll ans;
int t;
scanf("%d",&t);
while(t--)
{
cnt=0;
scanf("%s",s);
int len=strlen(s);
Manacher(s,len);
ans=0;
int n=len;
for(int i=1;i<2*len+2;i++){
if(Ma[i]!='#'){
int pos=i/2,now=(Mp[i]-1)/2;
b[pos]=min(pos+now,n);a[pos]=max(1,pos-now);
}
}
T[0]=0;
for(int i=1;i<=n;i++){
T[i]=update(T[i-1],1,n,b[i]);
}
for(int i=1;i<=n;i++){
ans+=query(T[a[i]-1],T[i-1],1,n,i);
}
printf("%lld\n",ans);
}
}