1. 程式人生 > >HDU5785 Interesting(manacher+差分陣列)

HDU5785 Interesting(manacher+差分陣列)

題意:一個串s,如果存在1<=i<=j<k<=|s|,且s[i,..j]和s[j+1,..k]都是迴文串,那麼對結果的貢獻就是i*k。求所有貢獻之和。

思路:對於每個位置i,設以i為末尾的迴文串的起點分別為s1,s2,...,以i+1為起點的迴文串的終點位置分別為t1,t2,....,那麼i位置對結果的貢獻=s1 * t1 + s1 * t2 +...+ s2 * t1 + s2 * t2 + ... = s1 * (t1 + t2 + ...) + s2 * (t1 + t2 + ...) + ... = (s1 + s2 + ...) * (t1 + t2 +...)。

因此處理出以每個位置i為終點的迴文串的起點位置之和,記為sum1[i],以i為起點的迴文串的集合的終點位置之和,記為sum2[i]。

先用manacher處理出每個點的迴文半徑,每個迴文串對迴文半徑範圍內都會產生貢獻,先累加每個迴文串對範圍內的貢獻,再區間求和即可。因為這裡RMQ的更新和查詢是分離的,所以可以用差分陣列來寫。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <cstdlib>
#include <set>
#include <map>
#include <vector>
#include <string>
using namespace std;
typedef long long ll;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn = 1000005;
const int mod = 1000000007;
const int N = 26; // 字符集大小

int n;
int manacher(char s[], char str[], int p[]){
    int len = n;
    str[0] = '*';//第一位是*,最後一位是'\0',防止下面的while越界
    for(int i = 0; i <= len; ++i){//初始化
        str[i * 2 + 1] = '#';
        str[i * 2 + 2] = s[i];
    }

    len = 2 * len + 1;
    int pos = 0, maxlen = 1;
    p[0] = 1;
    for(int i = 2; i < len; ++i){
        if(i < p[pos] + pos){
            p[i] = min(pos + p[pos] - i, p[pos * 2 - i]);
        } else {
            p[i] = 1;
        }
        while(str[i - p[i]] == str[i + p[i]]){
            ++p[i];
        }
        if(i + p[i] > pos + p[pos]){
            pos = i;//更新位置
        }
        if(p[i] > maxlen){
            maxlen = p[i];//更新結果
        }
    }
    return maxlen - 1;
}
char s[maxn], str[maxn * 2];
int p[maxn * 2];

ll cf1[maxn], cf2[maxn], sum1[maxn], sum2[maxn];
ll c1[maxn], c2[maxn], cnt1[maxn], cnt2[maxn];
// c1和c2記錄位置i被迴文串貢獻的次數
ll qz1[maxn], qz2[maxn];
int main() {
    while (~scanf("%s", s)) {
        n = strlen(s);
        for (int i = 0; i <= n + 1; ++i) {
            cf1[i] = cf2[i] = sum1[i] = sum2[i] = 0;
            c1[i] = c2[i] = cnt1[i] = cnt2[i] = 0;
        }
        manacher(s, str, p);
        int l = n << 1;
        for (int i = 2, r, j; i <= l; ++i) {
            r = p[i] >> 1;// 當前迴文串半徑
            j = i >> 1; //當前字元的實際下標
            if ((i & 1) && p[i] > 1) {// 處理偶數長度迴文串
                //區間[j + 1, j + r]加上i
                cf1[j + 1] += i; 
                cf1[j + r + 1] -= i; 
                
                c1[j + 1] += 1;
                c1[j + r + 1] -= 1;
                
                //區間[j - r + 1, j] 加上i
                cf2[j - r + 1] += i;
                cf2[j + 1] -= i;
                
                c2[j - r + 1] += 1;
                c2[j + 1] -= 1;
            } else if (((i ^ 1) & 1)) {// 處理奇數長度迴文串
                //區間[j, j + r - 1]加上i
                cf1[j] += i;
                cf1[j + r] -= i;
                c1[j] += 1;
                c1[j + r] -= 1;

                //區間[j - r + 1, j] 加上i
                cf2[j - r + 1] += i;
                cf2[j + 1] -= i;
                c2[j - r + 1] += 1;
                c2[j + 1] -= 1;
            }
        }

        for (int i = 1; i <= n; ++i) {
            sum1[i] = ((sum1[i - 1] + cf1[i] % mod + mod) % mod + mod) % mod;
            sum2[i] = ((sum2[i - 1] + cf2[i] % mod + mod) % mod + mod) % mod;
            cnt1[i] = ((cnt1[i - 1] + c1[i]) % mod + mod) % mod;
            cnt2[i] = ((cnt2[i - 1] + c2[i]) % mod + mod) % mod;
        }

        for (int i = 1; i <= n; ++i) {
            sum1[i] = (sum1[i] - cnt1[i] * i % mod + mod + mod) % mod;
            sum2[i] = (sum2[i] - cnt2[i] * i % mod + mod + mod) % mod;
        }

        ll ans = 0;
        for (int i = 1; i <= n; ++i) {
            ans = (ans + sum1[i] * sum2[i + 1] % mod) % mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}