HDU 5785 Interesting(Manacher)

8 篇文章 0 订阅

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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值