题意是给出一个字符串,找出满足条件的子串。
子串S的要求:
1.长度为S[1..3n−2] (n≥2)
2. S[i]=S[2n−i]=S[2n+i−2](1≤i≤n)
解题思路:从要求可以看出要求的子串是两个回文子串的叠加。
即子串可以看成 S[1...i...j...3n-2] (其中i=n; j=2n-i) ,S[1...j] 和S[i...3n-2] 分别为回文串,并且长度均为奇数。
用Manacher可以求出以i和j为中点的最大回文串半径 Mp[i]和Mp[j](不包含本身)
然后问题就可以转化为求i,j对满足条件:
1. i<j
2.i+Mp[i]>=j
3.j-Mp[j]<=j
即对每个i,求区间[i+1,i+Mp[i]]中j的个数,满足j-Mp[j]<=i
双条件区间求个数,可以用主席树
本题由于i是从小到大循环,可以对树状数组进行预处理,每次把j-Mp[j]==i的位置更新,再求区间个数。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define N 500010
char Ma[N*2];
int Mp[N*2],s[N],n;
struct DD{
int num,v;
};
void Manacher(char *s,int len){
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;
}
}
}
inline int lowbit(int x){ return x&(-x); }
LL getsum(int x){
LL ret=0;
while (x > 0){
ret+=s[x];
x=x-lowbit(x);
}
return ret;
}
void updata(int x,int y){
while (x <= n+1){
s[x]+=y;
x=x+lowbit(x);
}
}
bool cmp(DD & xx,DD & yy){
if (xx.v == yy.v) return xx.num<yy.num;
else return xx.v<yy.v;
}
int main(){
char c[N];
int T,i,j;
LL ans;
scanf("%d",&T);
while (T--){
scanf("%s",c);
n=strlen(c);
Manacher(c,n);
for (i=1; i<=n; i++){
Mp[i]=Mp[i*2]/2-1;
// printf("%d ",Mp[i]);
}
//printf("\n");
memset(s,0,sizeof(s));
ans=0;
DD a[N];
for (i=1; i<=n; i++){
j=i-Mp[i];
a[i].num=i;
a[i].v=j;
//updata(i,1);
}
sort(a+1,a+1+n,cmp);
j=1;
for (i=1; i<=n; i++){
while (a[j].v<=i && j<=n){
updata(a[j].num,1);
j++;
}
if (Mp[i] < 1) continue;
ans+=(getsum(i+Mp[i])-getsum(i));
}
/* for (i=1; i<=n; i++){
updata(i-Mp[i],1);
}
int sum[N];
ans=0;
memset(sum,0,sizeof(sum));
for (i=1; i<=n; i++){
sum[i+Mp[i]]++;
}
for (i=n; i>0; i--){
updata(i-Mp[i],-1);
printf("%d %d\n",sum[i],getsum(i));
if (sum[i] > 1) ans+=(LL)(sum[i]-1)*(LL)getsum(i-Mp[i]);
}*/
printf("%lld\n",ans);
}
return 0;
}