bzoj2565题解

1 篇文章 0 订阅
1 篇文章 0 订阅

不会manacher的先去学一下。

首先加入特殊字符,用manacher处理一遍,让pi代表以i为中心的回文串半径(不包括i本身)。

假设某个双回文串的两个回文中心为i、j (i<j) ,那么有pi+pj>=j-i-1。

化简一下,得到i+pi>=j-pj-1。

经过计算,我们知道,在添加特殊字符的串里面,这个回文串的长度为(j-i-1)*2+2=2(j-i)。

那么在原串中,这个双回文串的长度怎么算呢?

考虑一下特殊字符的出现位置、次数,可以得到是j-i。

于是i一定时,就要让j尽量大。

枚举i,这时i+pi就确定了,只需求出最大的j使j-pj-1<=i+pi。

于是需要一个数据结构,可以求出在某个区间中,小于某个值且最靠右的数在哪里。我写了一个奇怪的线段树,不知道有没有更好的方法。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ele int
using namespace std;
#define maxn 200010
struct node{
    ele _min,_max;
    node *l,*r;
}pool[maxn<<2];
char s1[maxn],s[maxn];
ele n,cnt,p[maxn];
node *root;
node *build(ele l,ele r){
    node *q=&pool[cnt++];
    if (l==r){
        q->_min=q->_max=(l-1)-p[l-1]-1;
        q->l=q->r=NULL;
    }
    else{
        ele mid=(l+r)>>1;
        q->l=build(l,mid);
        q->r=build(mid+1,r);
        q->_min=min(q->l->_min,q->r->_min);
        q->_max=max(q->l->_max,q->r->_max);
    }
    return q;
}
ele query(node *x,ele a,ele b,ele l,ele r,ele k){
    ele mid=(a+b)>>1;
    if (l<=a && b<=r){
        if (x->_min>k) return -1;
        if (a==b) return a-1;
        if (x->r->_min<k) return query(x->r,mid+1,b,l,r,k);
        return query(x->l,a,mid,l,r,k);
    }
    ele lans=-1,rans=-1;
    if (l<=mid) lans=query(x->l,a,mid,l,r,k);
    if (mid<r) rans=query(x->r,mid+1,b,l,r,k);
    if (rans!=-1) return rans;
    return lans;
}
int main(){
    scanf("%s",s1);
    n=strlen(s1);
    s[0]='~';
    for (int i=0; i<n; ++i)
        s[i<<1|1]='#',s[(i<<1)+2]=s1[i];
    s[n<<1|1]='#'; s[(n<<1)+2]='!';
    n=(n<<1)+3;
    p[0]=0;
    ele id=0,mx=0;
    for (int i=1; i<n; ++i){
        if (i<=mx)
            p[i]=min(p[id*2-i],mx-i);
        else p[i]=0;
        for (; s[i-p[i]-1]==s[i+p[i]+1]; ++p[i])
            ;
        if (i+p[i]>mx){
            id=i;
            mx=i+p[i];
        }
    }
    root=build(1,n);
    ele ans=0;
    for (int i=0; i<n; ++i){
        ele j=query(root,1,n,i+1,n,i+p[i]);
        ans=max(ans,j-i);
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值