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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值