【后缀数组+Manacher】BZOJ3676(Apio2014)[回文串]题解

题目概述

给出一个字符串,定义一个回文子串的权值为长度 × 出现次数。求最大权值。

解题报告

求回文子串可以用Manacher,每当找到一个回文子串,就用后缀数组求出其出现次数。因为是求子串出现次数,所以构造 height 再二分就可以 O(log2n) 查找。

本质不同回文子串的个数是 O(n) 的,所以效率是 O(nlog2n)

由于太蒟蒻,代码自带大常数,跑了19936ms……勉强卡过……

示例程序

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxl=600000,Log=20;

int n,p[maxl+5];LL ans;char s[maxl+5],now[maxl+5];
int ha[maxl+5],t[maxl+5],rk[maxl+5],SA[maxl+5],RMQ[maxl+5][Log+5];

inline void Sort(int n,int m){
    for (int i=1;i<=m;i++) ha[i]=0;for (int i=1;i<=n;i++) ha[rk[i]]++;
    for (int i=1;i<=m;i++) ha[i]+=ha[i-1];for (int i=n;i;i--) SA[ha[rk[t[i]]]--]=t[i];
}
void make_SA(){
    int m=256;for (int i=1;i<=n;i++) rk[i]=s[i],t[i]=i;Sort(n,m);
    for (int k=1,p=0;p<n;k<<=1,m=p){
        p=0;for (int i=n-k+1;i<=n;i++) t[++p]=i;for (int i=1;i<=n;i++) if (SA[i]>k) t[++p]=SA[i]-k;
        Sort(n,m);memcpy(t,rk,sizeof(t));rk[SA[1]]=p=1;
        for (int i=2;i<=n;rk[SA[i++]]=p) p+=(t[SA[i-1]]==t[SA[i]]&&t[SA[i-1]+k]==t[SA[i]+k])^1;
    }
    for (int i=1,k=0;i<=n;i++){
        if (k) k--;if (rk[i]==1) continue;int j=SA[rk[i]-1];
        while (s[i+k]==s[j+k]) k++;RMQ[rk[i]][0]=k;
    }
    for (int j=1;(1<<j)<=n;j++)
    for (int i=2;i<=n-(1<<j)+1;i++)
        RMQ[i][j]=min(RMQ[i][j-1],RMQ[i+(1<<j-1)][j-1]);
}
inline int getMIN(int L,int R) {int j=log2(R-L+1);return min(RMQ[L][j],RMQ[R-(1<<j)+1][j]);}
inline void Find(int x,int len){
    int L=x+1,R=n,r;
    if (RMQ[x+1][0]<len) R=x; else
    for (int mid=L+(R-L>>1);L<=R;mid=L+(R-L>>1))
        if (len<=getMIN(x+1,mid)) L=mid+1; else R=mid-1;
    r=R;L=1;R=x-1;
    if (RMQ[x][0]<len) L=x; else
    for (int mid=L+(R-L>>1);L<=R;mid=L+(R-L>>1))
        if (len<=getMIN(mid+1,x)) R=mid-1; else L=mid+1;
        ans=max(ans,(LL)(r-L+1)*len);
}
void Manacher(){
    int pos=0,R=0;
    for (int i=1;i<=n*2+1;i++){
        if (i<R) p[i]=min(p[pos*2-i],R-i); else p[i]=1;
        while (1<=i-p[i]&&i+p[i]<=n*2+1&&now[i-p[i]]==now[i+p[i]]) p[i]++;
        if (i+p[i]>R){
            for (int r=R+(R&1),l;r<i+p[i];r+=2) l=i*2-r,Find(rk[l>>1],(r-l>>1)+1);
            pos=i;R=i+p[i];
        }
    }
}
int main(){
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%s",s+1);n=strlen(s+1);
    for (int i=1;i<=n;i++) now[i*2-1]='%',now[i*2]=s[i];now[n*2+1]='%';
    return make_SA(),Manacher(),printf("%lld\n",ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值