[APIO2014]回文串

Description
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。

Input
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。

Output
输出一个整数,为逝查回文子串的最大出现值。

Sample Input 1
abacaba

Sample Output 1
7

Sample Input 2
www

Sample Output 2
4

HINT
\(\lvert s \rvert\) 表示字符串 s 的长度。
数据满足1≤ \(\lvert s\rvert\) ≤300000。


SA+Manacher

首先对原串处理出Height数组,同时求出以每个点作为回文中心所能扩张的最大长度

然后暴力枚举,对于每个作为回文中心的点,枚举回文串长度,然后在Height数组上二分,求出L,R,那么R-L+1就是出现次数

然后这样会TLE

考虑一个贪心的思想,对于每个作为回文串最左/右端的点,我们直接使用最大扩张长度即可

至于正确性?如果存在较小的长度使得其可以出现两次,那么我们必然可以在第二次出现的地方枚举到这种情况

然后注意求区间Height最小值,不能用线段树(可能会TLE),应该使用RMQ,复杂度为\(O(n\log n)\)

(然而我代码里写的是作为回文中心的……随手Hack,但是过了)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1;char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-'),x=-x;
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=3e5;
int n,m=26;
bool ck(int *r,int x,int y,int l){return r[x]==r[y]&&r[x+l]==r[y+l];}
void Suffix_sort(int *A,int *SA){
    static int sum[N+10],buf[N+10];
    memset(buf,0,sizeof(buf));
    int *x=A,*y=buf;
    for (int i=1;i<=m;i++)  sum[i]=0;
    for (int i=1;i<=n;i++)  sum[x[i]]++;
    for (int i=1;i<=m;i++)  sum[i]+=sum[i-1];
    for (int i=n;i>0;i--)   SA[sum[x[i]]--]=i;
    for (int l=1,p=0,top=0;p<n;l<<=1,m=p,top=0){
        for (int i=n-l+1;i<=n;i++)  y[++top]=i;
        for (int i=1;i<=n;i++)  if (SA[i]>l)    y[++top]=SA[i]-l;
        for (int i=1;i<=m;i++)  sum[i]=0;
        for (int i=1;i<=n;i++)  sum[x[y[i]]]++;
        for (int i=1;i<=m;i++)  sum[i]+=sum[i-1];
        for (int i=n;i>0;i--)   SA[sum[x[y[i]]]--]=y[i];
        swap(x,y),x[SA[1]]=p=1;
        for (int i=2;i<=n;i++)  x[SA[i]]=ck(y,SA[i-1],SA[i],l)?p:++p;
    }
}
int A[N+10],SA[N+10],Rank[N+10],H[N+10],p[(N<<1)+10];
void Get_height(char *s){
    for (int i=1,k=0,j;i<=n;H[Rank[i++]]=k)
    for (k?k--:0,j=SA[Rank[i]-1];s[i+k]==s[j+k];k++);
}
void Manacher(char *s){
    static char T[(N<<1)+10];
    for (int i=1;i<=n;i++)  T[i<<1]=s[i],T[i<<1|1]='#';
    int len=n<<1|1;
    T[0]='%',T[1]='#',T[len+1]='$';
    int Max=0,ID=0;
    for (int i=1;i<=len;i++){
        p[i]=Max>i?min(p[ID*2-i],Max-i):1;
        while (T[i+p[i]]==T[i-p[i]])    p[i]++;
        if (i+p[i]>Max) Max=i+p[ID=i];
    }
}
int rmq[20][N+10],g[N+10];
void RMQ(){
    for (int i=1;i<=n;i++)  rmq[0][i]=H[i];
    for (int j=1;j<20;j++)
        for (int i=1;i<=n;i++)
            if (i+(1<<j)-1<=n)
                rmq[j][i]=min(rmq[j-1][i],rmq[j-1][i+(1<<(j-1))]);
    for (int i=2;i<=n;i++)  g[i]=g[i-1]+(2<<g[i-1]<=i);
}
bool check(int l,int r,int k){
    int jp=g[r-l],tmp=min(rmq[jp][l+1],rmq[jp][r-(1<<jp)+1]);
    return tmp>=k;
}
int work(int x,int len){
    int L,R,l,r;
    l=1,r=x-1;
    while (l<=r){
        int mid=(l+r)>>1;
        if (check(mid,x,len))   r=mid-1;
        else    l=mid+1;
    }L=l;
    l=x+1,r=n;
    while (l<=r){
        int mid=(l+r)>>1;
        if (check(x,mid,len))   l=mid+1;
        else    r=mid-1;
    }R=r;
    return R-L+1;
}
int main(){
    static char s[N+10];
    scanf("%s",s+1);
    n=strlen(s+1);
    for (int i=1;i<=n;i++)  A[i]=s[i]-'a'+1;
    Suffix_sort(A,SA);
    for (int i=1;i<=n;i++)  Rank[SA[i]]=i;
    Get_height(s),RMQ();
    Manacher(s);
    ll Ans=0;
    for (int i=1;i<=(n<<1|1);i++){
        if (p[i]==1)    continue;
        int x=(i>>1)-(p[i]>>1)+1;
        Ans=max(Ans,1ll*work(Rank[x],p[i]-1)*(p[i]-1));
    }
    printf("%lld\n",Ans);
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/10578316.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值