Interesting (manacher + 前缀和处理)

  题意:相邻的两端回文串的价值为两个回文串总的区间左端点 × 区间右端点。然后计算目标串中所有该情况的总和。

  思路:首先用manacher求出所有中心点的最大半径,然后我们知道对于左区间我们把贡献记录在右端点,右区间记录在左端点。然后细节的我就不太懂了。迷~

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 +7;
const int mod  = 1e9 + 7;
long long rad[maxn], L[maxn], R[maxn], cnt1[maxn], cnt2[maxn];
char in[maxn], str[maxn];

void manacher(){
    int len = strlen(in), l = 0;
    str[l ++] = '$'; str[l ++] = '#';
    for(int i = 0; i < len; i ++)
        str[l ++] = in[i], str[l ++] = '#';
    str[l] = 0;
    int mx = 0, id = 0;
    for(int i = 0; i < l; i ++) {
        rad[i] = mx > i ? min(rad[2 * id - i], 1LL * mx - i) : 1;
        while(str[i + rad[i]] == str[i - rad[i]]) rad[i] ++;
        if(i + rad[i] > mx){
            mx = i + rad[i];
            id = i;
        }
    }
}

int main(){

    while(~scanf("%s", in)) {
        int len = strlen(in);
        manacher();
//        for(int i = 0; i < 2 * len + 2; i ++) printf(" %d ", rad[i]);
        memset(cnt1, 0, sizeof(cnt1));
        memset(cnt2, 0, sizeof(cnt2));
        for(int i = 1; i <= 2 * len; i ++){
            cnt1[i - rad[i] + 1] += i; cnt1[i + 1] -= i;
            cnt2[i - rad[i] + 1] ++; cnt2[i + 1] --;
        }
        for(int i = 1; i <= 2 * len; i ++){
            cnt1[i] += cnt1[i - 1], cnt2[i] += cnt2[i - 1];
            if(i % 2 == 0) R[i/2] = (cnt1[i] - i / 2 * cnt2[i]) % mod;
        }
        memset(cnt1, 0, sizeof(cnt1));
        memset(cnt2, 0, sizeof(cnt2));
        for(int i = 1; i <= 2 * len; i ++) {
            cnt1[i + rad[i]] -= i; cnt1[i] += i;
            cnt2[i + rad[i]] --; cnt2[i] ++;
        }
        for(int i = 1; i <= 2 * len; i ++) {
            cnt1[i] += cnt1[i - 1]; cnt2[i] += cnt2[i - 1];
            if(i % 2 == 0) L[i / 2] = (cnt1[i] - i / 2 * cnt2[i]) % mod;
        }
        long long ans = 0;
        for(int i = 0; i < len; i ++) ans = (ans + L[i] * R[i + 1] % mod) % mod;
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wethura/p/9898071.html