【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{egin{matrix} j-i leq len[i] \ j-i leq len[j] \ end{matrix} ight. )

移动式子,可得

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

将其转化为区间 ([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
", 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
", res);

//        for (int i = 1; i <= n; i++) {
//            printf("%d ", ren[i]);
//        }
//        printf("
");
    }
}
原文地址:https://www.cnblogs.com/tudouuuuu/p/14110588.html