1. 程式人生 > 實用技巧 >【HDU-6230/2017CCPC哈爾濱A】Palindrome(式子轉換+馬拉車+主席樹)

【HDU-6230/2017CCPC哈爾濱A】Palindrome(式子轉換+馬拉車+主席樹)

題目連結:https://vjudge.net/problem/HDU-6230、

題目大意

給定一個字串,求這個字串中有多少個一個半字串

一個半字串:類似於1234543212345。

花絮

模擬賽時一直只思考了 123454321 怎麼通過 \(O(1)\) 的時間複雜度搞出後面的串,導致直接卡死。

思路

1234543212345 是按照5和1對稱的,那麼在保證5和1是迴文串的狀態下,要能夠之間相互覆蓋。

設5的位置為 \(i\),1的位置為 \(j\), 要求 \(i \leq j\),則可得公式

\( \left\{\begin{matrix} j-i \leq len[i] \\ j-i \leq len[j] \\ \end{matrix}\right. \)

移動式子,可得

\( \left\{\begin{matrix} j \leq i+len[i] \\ j-len[j] \leq i \\ \end{matrix}\right. \)

將其轉化為區間 \([i+1, i+len[i]]\) 內求有多少個數,使得 \(j-len[j] \leq i\)

AC程式碼

#include <bits/stdc++.h>

typedef long long ll;
using namespace std;
const int MAXN = 5e5 + 5;

class HJT {
public:
    int ch[MAXN * 70][2], sum[MAXN * 70];
    int tot;

    void init() { tot = 0; }

    inline void push_up(int rt) {
        sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]];
    }

    int update(int rt, int pos, int val, int be, int en) {
        int nrt = ++tot;
        ch[nrt][0] = ch[nrt][1] = sum[nrt] = 0;
        if (be == en) {
            sum[nrt] = sum[rt] + val;
            return nrt;
        }
        int mid = (be + en) >> 1;
        if (pos <= mid) {
            ch[nrt][0] = update(ch[rt][0], pos, val, be, mid);
            ch[nrt][1] = ch[rt][1];
        } else {
            ch[nrt][0] = ch[rt][0];
            ch[nrt][1] = update(ch[rt][1], pos, val, mid + 1, en);
        }
        push_up(nrt);
        return nrt;
    }

    int query(int lrt, int rrt, int R, int be, int en) {
        if (R == en) return sum[rrt] - sum[lrt];
        int mid = (be + en) >> 1;
        if (R <= mid) {
            return query(ch[lrt][0], ch[rrt][0], R, be, mid);
        } else {
            return sum[ch[rrt][0]] - sum[ch[lrt][0]] + query(ch[lrt][1], ch[rrt][1], R, mid + 1, en);
        }
    }

} tree;


void Manacher(char s[], int len, char A[], int B[]) {
    int l = 0;
    A[l++] = '$';
    A[l++] = '#';
    for (int i = 0; i < len; i++) {
        A[l++] = s[i];
        A[l++] = '#';
    }
    A[l] = 0;
    int mx = 0, id = 0;
    for (int i = 0; i < l; i++) {
        B[i] = mx > i ? std::min(B[2 * id - i], mx - i) : 1;
        while (A[i + B[i]] == A[i - B[i]]) B[i]++;
        if (i + B[i] > mx) mx = i + B[i], id = i;
    }
}

char A[MAXN * 2];
int B[MAXN * 2];

char str[MAXN];
int ren[MAXN], root[MAXN];

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%s", str + 1);
        int n = strlen(str + 1);
        Manacher(str + 1, n, A + 1, B + 1);

        int maxR = 0;
        for (int i = 1; i <= n; i++) ren[i] = (B[i * 2 + 1] - 2) >> 1, maxR = max(maxR, i - ren[i]);

        tree.init();
        root[0] = 0;
        for (int i = 1; i <= n; i++) {
            root[i] = tree.update(root[i - 1], i - ren[i], 1, 1, maxR);
        }
        
        ll res = 0;
        for (int i = 1; i <= n; i++) {
            if (ren[i] == 0) continue;
          //  printf("%d %d %d %d\n", i, i, i+ren[i], tree.query(root[i], root[i+ren[i]], i, 1, maxR));
            res = res + tree.query(root[i], root[i+ren[i]], i, 1, maxR);
        }
        printf("%lld\n", res);

//        for (int i = 1; i <= n; i++) {
//            printf("%d ", ren[i]);
//        }
//        printf("\n");
    }
}