题意
给你一个1e5的字符串,让你求出子串的数量,使得子串满足:
长度为3n−23n−2,且[1,2n−1][1,2n−1]和[n,3n−2][n,3n−2]都是回文串。
思路
这两个回文串长度都是奇数 manachar求出每个位置的最长回文半径
那么假设两个回文中心为i j 那么需满足 i + r[i] ≥ j && j − r[j] ≤ i 用树状数组离线求答案
G[i - r[i]].push_back(i);//G[这个位置开始的].push_back(覆盖到哪里);
遍历字符串 每次将i之前的所能覆盖到的位置update(+1)
ans += sum(当前i的边界 - i);//满足上式的区段
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std;
const int MaxN = 1e6 + 5;
typedef long long LL;
int t,n,p[MaxN],tree[MaxN],r[MaxN];
char s[MaxN],S[MaxN];
vector<int> G[MaxN];
void getS(){
n = strlen(s + 1);
S[0] = '@';
for(int i = 1;i <= n; i++){
S[i * 2 - 1] = '#';
S[i * 2] = s[i];
}
S[n * 2 + 1] = '#';
S[n * 2 + 2] = '$';
n = n * 2 + 2;
}
void manachar(){
int mx = 0,C = 0,ans = 0;
for(int i = 1;i < n; i++){
if(mx > i) p[i] = min(mx - i,p[2 * C - i]);
else p[i] = 1;
while(S[i + p[i]] == S[i - p[i]]) p[i]++;
if(p[i] + i > mx){
mx = p[i] + i;
C = i;
// if(p[i] > ans){
// ans = p[i];
// A = i;
// }
}
}
}
void update(int x){
for(int i = x;i <= n; i += i & (-i)){
tree[i] += 1;
}
}
LL sum(int x){
LL cur = 0;
for(int i = x;i;i -= i & (-i)){
cur += tree[i];
}
return cur;
}
int main()
{
scanf("%d",&t);
while(t--){
scanf("%s",s + 1);
memset(p,0,sizeof p);
memset(r,0,sizeof r);
getS();
manachar();
for(int i = 0;i <= n; i++){
tree[i] = 0;
G[i].clear();
}
for(int i = 1;i <= n / 2; i++){
r[i] = p[i * 2] / 2;
G[i - r[i]].push_back(i);
}
LL ans = 0;
for(int i = 1;i <= n / 2; i++){
for(int j = 0;j < G[i - 1].size(); j++) update(G[i - 1][j]);
ans += sum(i + r[i] - 1) - sum(i);
}
printf("%lld\n",ans);
}
}