版权声明:欢迎大家转载,转载请注明出处 https://blog.csdn.net/hao_zong_yin/article/details/82669469
根据题目的要求可知我们只需要长度为奇数的回文串
首先Manacher求出以每个字符为中心的最长回文半径(我求出来不包括自己,当然包括自己也可以,只是i+p[i]和i+p[i]-1的区别),然后枚举每个字符s[i]作为回文串中心,对于每个i,我们要求的是区间(i,i+p[i]]中有多少个j满足j-p[j]<=i,这个看似很棘手,其实可以利用一下我们对i的枚举顺序,因为我们是从小到大枚举的i,所以可以先预处理出每个i对应的j(满足j-p[j] == i),每枚举到一个i,首先更新这个i对应的所有j,将位置j的值+1(初始全部为0),然后求一下区间(i,i+p[i]]的和,将其加到ans中,最后输出ans即可,更新和求和都可以用树状数组实现
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
int T;
char t[maxn], s[maxn];
int n, p[maxn];
void init() {
int len = strlen(t);
s[0] = '$'; s[1] = '#';
n = 2;
for (int i = 0; i < len; i++) {
s[n++] = t[i]; s[n++] = '#';
}
s[n] = 0;
}
void manacher() {
int id, mx = 0;
for (int i = 1; i < n; i++) {
if (i < mx) p[i] = min(p[2*id-i], mx-i);
else p[i] = 1;
while (s[i-p[i]] == s[i+p[i]]) p[i]++;
if (mx < i + p[i]) { id = i; mx = i + p[i]; }
}
int m = n;
n = 0;
for (int i = 1; i < m; i++) if (s[i] != '#') p[++n] = p[i]/2 - 1;
}
ll C[maxn];
int lowbit(int x) { return x & (-x); }
ll sum(int x) {
ll ans = 0;
while (x > 0) { ans += C[x]; x -= lowbit(x); }
return ans;
}
void add(int x, int d) {
while (x <= n) { C[x] += d; x += lowbit(x); }
}
vector<int> v[maxn];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%s", t);
init();
manacher();
for (int i = 1; i <= n; i++) v[i].clear();
for (int i = 1; i <= n; i++) v[i-p[i]].push_back(i);
for (int i = 1; i <= n; i++) C[i] = 0;
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < v[i].size(); j++) add(v[i][j] ,1);
ans += (sum(i + p[i]) - sum(i));
}
printf("%lld\n", ans);
}
return 0;
}