hdu 5785 manacher回文处理 + 扫描线

分析:

  • 首先考虑遍历所有的两头的 i,k ,然后去找他们中间的一个点 c ,让这个点把str[i...k]且成两段回文串。
    这样是可行的,但是复杂度枚举两头已经是 O(n2) 了。
  • 然后考虑换一个思路,枚举所有的中间的隔代 c ,然后以c为隔代的满足条件的所有回文串的两头。
    这样发现,用所有以 c 左边元素为结尾的回文串的左边界和乘以以c右边元素为开始的所有回文串的右边界和,就是 c 为隔代的贡献。

我们要o(n)得求出以上两个记录和的数组。考虑用manacher算法处理字符串之后会出现很多回文串,可以知道它们的左右端点和中心。这些都是一些回文区间,我们可以转化为区间覆盖问题,把和拆解,然后上扫描线。
扫描线可以带权的。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define pr(x) cout << #x << ": " << x << "  " 
#define pl(x) cout << #x << ": " << x << endl;

struct jibancanyang
{
    static const int maxn = int(2e6) +7, mod = int(1e9) + 7;
    char str[maxn], aim[maxn * 2];
    int ti, len, P[maxn * 2];
    long long cntl[maxn], suml[maxn], cntr[maxn], sumr[maxn];
    long long ans;

    void pre() {
        len = strlen(str);
        ti = 0;
        aim[ti++] = '^';
        for (int i = 0; i < len; ++i) aim[ti++] = '#', aim[ti++] = str[i];
        aim[ti++] = '#', aim[ti++] = '$';
    }

    void aft() {
        memset(suml, 0, sizeof(suml));
        memset(sumr, 0, sizeof(suml));
        memset(cntl, 0, sizeof(suml));
        memset(cntr, 0, sizeof(suml));
        for (int i = 2; i < ti - 2; ++i) {
            int c = i;
            int l, r;
            l = i - (P[i] - 1), r = i + (P[i] - 1); 
            cntl[c + 1]++, cntl[r + 2]--;
            cntr[c - 1]++, cntr[l - 2]--;
            suml[c + 1] += i, suml[r + 2] -= i;
            sumr[c - 1] += i, sumr[l - 2] -= i;
        }
        long long sx = 0, sm = 0;
        for (int i = 0; i < ti; ++i) {
            sx += cntl[i];
            sm += suml[i]; 
            if (i % 2 && i > 2 && i < ti - 3) {
                suml[i] = ((sm - (i - 1 ) / 2 * sx) + mod) % mod;
            }
        }

        sx = 0, sm = 0;
        for (int i = ti - 1; i >= 0; --i) {
            sx += cntr[i];
            sm += sumr[i];
            if (i % 2 && i > 2 && i < ti - 3) {
                sumr[i] = ((sm - (i + 1) / 2 * sx) + mod) % mod;
            }
        }

        ans = 0;
        for (int i = 3; i < ti - 3; i += 2) {
            ans = ans + suml[i] * sumr[i] ;
            ans %= mod;
        }
    }

    void manacher() {
        pre();
        memset(P, 0, sizeof(P));
        int C = 0, R = 0;
        for (int i = 1; i < ti - 1; ++i) {
            int i_mirror = 2 * C - i;
            P[i] = (R > i) ? min((R - i), P[i_mirror]) : 0;
            while (aim[i + 1 + P[i]] == aim[i - 1 - P[i]]) ++P[i];
            if (i + P[i] > R) C = i, R = i + P[i];
        }
        aft();
    }

    void fun() {
        while (~scanf("%s", str)) manacher(), printf("%lld\n", ans);
    }

}ac;

int main()
{
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    ac.fun();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值