HDU5785 Interesting

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5785


【题意】已知一个string s,对于求满足s[i,j],s[j+1,k]都是回文串所有i*k的和。


【分析】用Manacher算法获取回文串的长度,相应转换成回文串半径。采用题解的方法,计算以i为起点的所有回文串终点和cntR[i],以及以i为终点的所有回文串起点和cntL[i]。于是答案就是遍历0到strlen(s)-1计算cntL[i]*cntR[i+1]的和。在计算cntR和cntL的时候,暴力做会超时,利用标记数组O(n)来处理才过的。这里对cntR的标记进行说明,cntL的标记同理。对于一个回文串s[l,r],标记sumtag[l]+=r,传递时sumtag[l+1]=sumtag[l]-1。由于标记只需要传到(l+r)/2,所以sum[(l+r)/2+1]-=(l+r)/2-1。考虑到区间重叠的问题,以sumcnt[l]标记覆盖l的区间个数,于是每次处理时sumcnt[l]++,sum[(l+r)/2+1]--。这样每次传递时改为sumtag[l+1]=sumtag[l]-sumcnt[l]。(ps:注意中间值的取值,这里仅作说明不一定正确,同时奇偶长度的串需要分开处理)。


【代码】

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define MAXN 1001000
const int mod=1e9+7;
int p[MAXN*2];
char str[MAXN*2];
LL sumtagR[MAXN];
LL sumcntR[MAXN];
LL sumtagL[MAXN];
LL sumcntL[MAXN];
LL cntL[MAXN];
LL cntR[MAXN];
int len,id;
void init(){
    memset(sumtagR,0,sizeof(sumtagR));
    memset(sumtagL,0,sizeof(sumtagL));
    memset(sumcntR,0,sizeof(sumcntR));
    memset(sumcntL,0,sizeof(sumcntL));
    len=strlen(str),id=0;
    for(int i=len;i>=0;i--){
        str[i+i+2]=str[i];
        str[i+i+1]='#';
    }
    str[0]='*';
    for(int i=2;i<2*len+1;++i){
        int l,r;
        if(p[id]+id>i)
            p[i]=min(p[2*id-i],p[id]+id-i);
        else
            p[i]=1;
        while(str[i-p[i]]==str[i+p[i]])
            ++p[i];
        if(id+p[id]<i+p[i])
            id=i;
        if(str[i]=='#'){
            l=i/2-(p[i]-1)/2+1,r=i/2+(p[i]-1)/2;
            //printf("%d %d %d %d\n",l,r,i/2+1,i/2);
            sumtagR[l]=(sumtagR[l]+r)%mod;
            sumtagR[i/2+1]=(sumtagR[i/2+1]-i/2)%mod;
            sumcntR[l]++;
            sumcntR[i/2+1]--;
            sumtagL[r]=(sumtagL[r]+l)%mod;
            sumtagL[i/2]=(sumtagL[i/2]-i/2-1)%mod;
            sumcntL[r]++;
            sumcntL[i/2]--;
        }
        else{
            l=i/2-(p[i]-1)/2,r=i/2+(p[i]-1)/2;
            //printf("%d %d %d %d\n",l,r,i/2+1,i/2-1);
            sumtagR[l]=(sumtagR[l]+r)%mod;
            sumtagR[i/2+1]=(sumtagR[i/2+1]-i/2+1)%mod;
            sumcntR[l]++;
            sumcntR[i/2+1]--;
            sumtagL[r]=(sumtagL[r]+l)%mod;
            sumtagL[i/2-1]=(sumtagL[i/2-1]-i/2-1)%mod;
            sumcntL[r]++;
            sumcntL[i/2-1]--;
        }
    }
    for(int i=1;i<=len;++i){
        cntR[i]=sumtagR[i];
        sumtagR[i+1]=((sumtagR[i+1]+sumtagR[i]-sumcntR[i])%mod+mod)%mod;
        sumcntR[i+1]=(sumcntR[i+1]+sumcntR[i])%mod;
    }
    for(int i=len;i;i--){
        cntL[i]=sumtagL[i];
        sumtagL[i-1]=((sumtagL[i-1]+sumtagL[i]+sumcntL[i])%mod+mod)%mod;
        sumcntL[i-1]=(sumcntL[i-1]+sumcntL[i])%mod;
    }
}
int main(){
    while(~scanf("%s",str)){
        init();
        LL ans=0;
        for(int i=1;i<len;++i)
            ans=(ans+(cntL[i]*cntR[i+1])%mod)%mod;
        cout<<ans<<endl;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值