卧薪尝胆,厚积薄发。
Palisection
Date: Fri Sep 21 08:35:44 CST 2018
In Category:
NoCategory
Description:
给一个字符串,求有相交部分的回文子串对有多少。
$1\le n \le 4000000$
Solution:
正难则反,相交的情况很复杂,不相交的更简单,考虑计算出所有的回文串个数
$sum$
再用
$C^{sum}_2-$
不相交的回文串对个数。
先用
$manacher$
预处理出所有位置的最长回文半径,然后计算两个数组,
$lef[i]$
和
$rig[i]$
分别表示以
$i$
为左
$/$
右端点的回文串个数那么总的不相交回文串对个数就是
$\begin{align}\sum_{i<j}rig[i]\times lef[j]\end{align}$
,但这样计算还是太慢了,把
$lef$
数组做一个后缀和,答案就变成了
$\begin{align}\sum_{i=1}^{n-1}rig[i]\times lef\_sufsum[i+1]\end{align}$
。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MOD 51123987
#define INV 25561994
int n;
#define MAXN 4000010
char c[MAXN];
int h[MAXN];
char s[MAXN];
int tot = 0;
typedef long long ll;
ll lef[MAXN],rig[MAXN];
int main()
{
scanf("%d",&n);
scanf("%s",c + 1);
s[++tot] = '#';
for(int i = 1;i <= n;++i)
{
s[++tot] = c[i];
s[++tot] = '#';
}
int pos = 1,maxr = 1;
ll sum = 0;
for(int i = 1;i <= tot;++i)
{
if(maxr > i)h[i] = min(h[pos * 2 - i],maxr - i + 1);
else h[i] = 1;
while(i - h[i] >= 1 && i + h[i] <= tot && s[i - h[i]] == s[i + h[i]])++h[i];
if(i + h[i] - 1 > maxr)
{
maxr = i + h[i] - 1;
pos = i;
}
sum = (sum + h[i] / 2) % MOD;
}
for(int i = 2;i < tot;++i)
{
if(i & 1)
{
lef[(i - h[i] + 2) / 2]++;lef[i / 2 + 1]--;
rig[i / 2 + 1]++;rig[(i + h[i] - 1) / 2 + 1]--;
}
else
{
lef[(i - h[i] + 2) / 2]++;lef[i / 2 + 1]--;
rig[i / 2]++;rig[(i + h[i]) / 2]--;
}
}
for(int i = 1;i <= n;++i)lef[i] = (lef[i - 1] + lef[i]) % MOD;
for(int i = 1;i <= n;++i)rig[i] = (rig[i - 1] + rig[i]) % MOD;
for(int i = n - 1;i >= 1;--i)lef[i] = (lef[i + 1] + lef[i]) % MOD;
long long ans = 0;
for(int i = 1;i < n;++i)
{
ans = (ans + rig[i] * lef[i + 1] % MOD) % MOD;
}
ans = (sum * (sum - 1) % MOD * INV % MOD - ans + MOD) % MOD;
cout << ans << endl;
return 0;
}
In tag:
字符串-manacher
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡