题目定义了 “一个半回文串”,其实就是形如abab,是一个长度3n-2的字符串,其中子串[1,2n-1]是长度为奇数的回文串,[n,3n-2]同样是长度为奇数的回文串。
那么所求可转化如下。
首先,因为回文串长度均为奇数,因此我们不需要考虑偶数长度的回文子串。
假设位置i的字符作为回文子串的中心时的最大回文半径为r[i](这个显然可以通过马拉车算法预处理出),则我们要求的是这样的 (i,j)的对数:
假设i>j,则有 i-r[i]>=j && j+r[j]>=i,因为只有这样的一对位置,才可以保证能形成符合要求的“一个半回文串”。
这个感觉很像是一个二维偏序问题。
对所有的(i-r[i],i)按i-r[i]从小到大排序,从小到大枚举j,因为只有i-r[i]小于等于当前j的i才可能与之形成符合要求的 (i,j),因此将所有i-r[i]小于等于当前j的i插入树状数组(即将对应位置+1),然后,统计在(j,j+r[j]]之间的和即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define mst(a,b) memset(a,b,sizeof(a))
#define ll long long
const int MAXN=5e5+5;
char Ma[MAXN];
int Mp[MAXN];
void Manacher(char s[],int len){
//因为只需要求奇数长度的回文串,所以除了起始字符外不用添加特殊字符 int l=len+1;Ma[0]='$';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;}}
}
int t;
int c[MAXN];
int lowbit(int x){return x&(-x);
}
void add(int x,int z=1){while(x<MAXN){c[x]+=z;x+=lowbit(x);}
}
int query(int x){int res=0;while(x>0){res+=c[x];x-=lowbit(x);}return res;
}
struct node{int d,pos; bool operator<(const node &p)const{//按差值排序 return d<p.d;}
}a[MAXN];
int main(){scanf("%d",&t);while(t--){scanf("%s",Ma+1);int len=strlen(Ma+1);Manacher(Ma,len);mst(c,0);for(int i=1;i<=len;i++){ Mp[i]--;}ll ans=0;int cnt=0;for(int i=len;i>=1;i--){ a[cnt].d=i-Mp[i];a[cnt].pos=i;cnt++;}sort(a,a+cnt);for(int i=1,j=0;i<len;i++){while(j<cnt&&a[j].d<=i){add(a[j].pos);j++;}ans+=query(i+Mp[i])-query(i);}cout<<ans<<"\n";}return 0;
}