HDU 6230 Palindrome——Manacher+树状数组

版权声明:欢迎大家转载,转载请注明出处 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;
}

猜你喜欢

转载自blog.csdn.net/hao_zong_yin/article/details/82669469