triple---manacher

题意

给定一个只含小写字母的字符串S.如果S[i……j]与S[(j+1)……k]均为回文串,则(i,j,k)是一个三元组。
对于所有的三元组,求: ik

数据范围

30%1|S|100
60%1|S|10000
100%1|S|1000000
多组数据,数据不超过5组.

题解

manacher+差分.
跑一遍manacher,找到每个位置的最远扩展距离,然后区间修改。
我们发现区间修改的值是一个等差数列,即便是用线段树维护,也逃不脱差分。
而且数据范围明显要求我们用O(n)的做法,所以线段树行不通。
于是我们可以用差分,一个数组记录等差首项,另一个记录公差的相关信息。
然而我考场上写的是 O(n2) 的暴力,居然有70分。
后来看了一下题解,就凭着感觉自己写了一份manacher+差分数组的代码,竟然A了。
具体细节,草稿纸上算几个例子就明白了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; 
#define MAXN 1005000
#define mod 1000000007
char s[MAXN],t[MAXN<<1];
int n,T,p[MAXN<<1];
long long a[MAXN],b[MAXN],c[MAXN],d[MAXN];
void ch1(int x,int l,int l2)
{
     //x,x-l+1 
     //x-1,x-l
     if(l2&1)
     {
         a[x]+=x; 
         a[x+l]-=x-l+1;
         b[x+1]-=1;
         b[x+l]+=1;
     }
     else
     {
         a[x]+=x-1;
         a[x+l]-=x-l;
         b[x+1]-=1;
         b[x+l]+=1;
     }
     return ;
}
void ch2(int x,int l,int l2)
{
     if(l2&1)
     {
         c[x-l+1]+=x+l-1;
         c[x+1]-=x;
         d[x-l+2]-=1;
         d[x+1]+=1;
     }
     else
     {
         c[x-l+1]+=x+l;
         c[x+1]-=x+1;
         d[x-l+2]-=1;
         d[x+1]+=1;
     }
     return ;
}
void solve()
{
    for(int i=1;i<=n;i++)
    {
        b[i]+=b[i-1];
        d[i]+=d[i-1];
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=a[i-1]+a[i];
        a[i]=a[i]+b[i];
        c[i]=c[i-1]+c[i];
        c[i]=c[i]+d[i];
    }
    return ;
}
void work()
{
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    memset(d,0,sizeof(d)); 
    scanf("%s",s+1);
    n=strlen(s+1);
    int pos=0;
    t[pos]='$';
    for(int i=1;i<=n;i++)
    {
        t[++pos]='#';
        t[++pos]=s[i];
    }
    t[++pos]='#';
    int id=0,mx=0; 
    for(int i=1;i<pos;i++)
    {
        p[i]=mx>i?min(mx-i,p[2*id-i]):1;
        while(t[i+p[i]]==t[i-p[i]])
        {
            ++p[i];
        }
        if(p[i]>1)
        { 
            ch1((i+1)>>1,p[i]>>1,p[i]-1);
            ch2(i>>1,p[i]>>1,p[i]-1);
        } 
        if(p[i]+i>mx)
        {
             mx=p[i]+i;
             id=i;
        }
    }
    solve();
    int ans=0;
    for(int i=1;i<n;i++)
    {
         ans+=1ll*a[i]%mod*c[i+1]%mod;
         ans-=ans>=mod?mod:0;
         //printf("i%d  a%d  c%d\n",i,a[i],c[i]);
    }
    printf("%d\n",ans);
    return ;
}
int main()
{
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
        work();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值