Description
给出一个字符串,问所有满足条件的i*k的和,i和k满足条件是指i到j和j+1到k是回文串
Input
多组用例,每组用例输入一个长度不超过1e6的字符串,以文件尾结束输入
Output
对于每组用例,输出满足条件的i*k的和,结果模1e9+7
Sample Input
aaa
abc
Sample Output
14
8
Solution
首先少不了跑一遍Manacher,对于每个i,求出两个值L[i]和R[i],L[i]表示i作为起点的回文串终点之和,R[i]表示i作为终点的回文串起点之和,那么sum(R[i]*L[i+1])即为答案,直接求L[i]和R[i]显然不行,需要看每个回文串对哪些L和R有贡献,一个以i为中心的回文串,设其长度为ma[i],那么对于i左边的j(j>=i-ma[i]),j~i+i-j是回文串,那么L[j]需要加上2*i-j,对于i右边的j(j<=i+ma[i]),i-(j-i)~j是回文串,那么R[j]需要加上2*i-j,由于用Manacher时在字符串中插入了其他字符,故要加上的应该是i+j/2,L和R求法差不多,下面只说L数组怎么求,因为对于每个i,需要更新[i-ma[i]+1,i]之间所有j的值,一个个更新显然会爆掉,所以用前缀和优化的方法,把要累加的i-j/2拆成i和j/2,前一部分就变成了区间累加i,后一部分可以先区间累加1然后对于每个j乘以j/2即可,故这个操作可以每次O(1)更新,最后O(n)求两边遍前缀和即可得到L数组,R数组同理,之后O(n)累加答案即可,总复杂度O(n)
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 1000000007ll
#define maxn 2222222
char ss[maxn];
int ma[maxn];
void Manacher(char *s,int len)
{
int l=0;
ss[l++]='$';
ss[l++]='#';
for(int i=0;i<len;i++)
{
ss[l++]=s[i];
ss[l++]='#';
}
ss[l]=0;
int mx=0,id=0;
for(int i=0;i<l;i++)
{
ma[i]=mx>i?min(ma[2*id-i],mx-i):1;
while(ss[i+ma[i]]==ss[i-ma[i]])ma[i]++;
if(i+ma[i]>mx)
{
mx=i+ma[i];
id=i;
}
}
}
char s[maxn];
ll num1[maxn],num2[maxn],l[maxn],r[maxn];
int main()
{
while(~scanf("%s",s))
{
int len=strlen(s);
Manacher(s,len);
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
for(int i=1;i<=2*len;i++)
{
num1[i-ma[i]+1]+=i,num1[i+1]-=i;
num2[i-ma[i]+1]++,num2[i+1]--;
}
for(int i=1;i<=2*len;i++)
{
num1[i]+=num1[i-1],num2[i]+=num2[i-1];
if(i%2==0)l[i/2]=(num1[i]-i/2*num2[i])%mod;
}
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
for(int i=1;i<=2*len;i++)
{
num1[i+ma[i]]-=i,num1[i]+=i;
num2[i+ma[i]]--,num2[i]++;
}
for(int i=1;i<=2*len;i++)
{
num1[i]+=num1[i-1],num2[i]+=num2[i-1];
if(i%2==0)r[i/2]=(num1[i]-i/2*num2[i])%mod;
}
ll ans=0;
for(int i=1;i<len;i++)
ans=(ans+l[i+1]*r[i]%mod)%mod;
printf("%I64d\n",ans);
}
return 0;
}