题意:一个串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;
}