[bzoj 3676][uoj #103]【APIO2014】Palindromes回文串 后缀数组+manachar

给你一个由小写拉丁字母组成的字符串 ss。我们定义 ss 的一个子串的存在值为这个子串在 ss 中出现的次数乘以这个子串的长度。

对于给你的这个字符串 ss,求所有回文子串中的最大存在值。
输入格式

一行,一个由小写拉丁字母(a~z)组成的非空字符串 ss。
输出格式

输出一个整数,表示所有回文子串中的最大存在值。
样例一
input

abacaba

output

7

explanation

用 ∣s∣∣s∣ 表示字符串 ss 的长度。

一个字符串 s1s2…s∣s∣s1s2…s∣s∣ 的子串是一个非空字符串 sisi+1…sjsisi+1…sj,其中 1≤i≤j≤∣s∣1≤i≤j≤∣s∣。每个字符串都是自己的子串。

一个字符串被称作回文串当且仅当这个字符串从左往右读和从右往左读都是相同的。

这个样例中,有 77 个回文子串 a,b,c,aba,aca,bacab,abacaba。他们的存在值分别为 4,2,1,6,3,5,74,2,1,6,3,5,7。

所以回文子串中最大的存在值为 77。
样例二
input

www

output

4

限制与约定

第一个子任务共 8 分,满足 1≤∣s∣≤1001≤∣s∣≤100。

第二个子任务共 15 分,满足 1≤∣s∣≤10001≤∣s∣≤1000。

第三个子任务共 24 分,满足 1≤∣s∣≤100001≤∣s∣≤10000。

第四个子任务共 26 分,满足 1≤∣s∣≤1000001≤∣s∣≤100000。

第五个子任务共 27 分,满足 1≤∣s∣≤3000001≤∣s∣≤300000。

时间限制:1

题目链接http://uoj.ac/problem/103

思路
后缀数组排序,可二分求长度相等的串的个数,利用manacher的一个性质更新答案:
“只有使mx变大的回文串,才是与之前所有回文子串不同的新串,否则一定可以由之前的回文串关于id对称得到”

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define ull unsigned long long
#define maxn 600005
#define inf 1000000000
using namespace std;
int n,r[maxn],cnt[maxn],x[maxn],y[maxn],sa[maxn],rank[maxn],h[maxn],p[maxn],f[maxn][20],lg2[maxn];
ll ans;
char s[maxn];
void SA(int n,int m)
{

    for(int i=0;i<m;i++) cnt[i]=0;
    for(int i=0;i<n;i++) cnt[x[i]=r[i]]++;
    for(int i=1;i<m;i++) cnt[i]+=cnt[i-1];
    for(int i=n-1;i>=0;i--)  sa[--cnt[x[i]]]=i;



    for(int j=1,p=1;p<n;j<<=1,m=p)
     {
            p=0;
        for(int i=n-j;i<n;i++) y[p++]=i;
        for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(int i=0;i<m;i++)   cnt[i]=0;
        for(int i=0;i<n;i++)   cnt[x[y[i]]]++;
        for(int i=1;i<m;i++) cnt[i]+=cnt[i-1];
        for(int i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]]=y[i];
            swap(x,y);
        x[sa[0]]=0;p=1;
            for(int i=1;i<n;i++)
            x[sa[i]]=((y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+j]==y[sa[i-1]+j])) ? p-1:p++;

        }

}
void get_H(int n)
{
    for(int i=1;i<=n;i++) rank[sa[i]]=i;
    int j=0,k=0;
    for(int i=0;i<n;h[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    return ;    
}
inline int rmq(int l,int r)
{
    if (l>r) return inf;
    int t=lg2[r-l+1];
    return min(f[l][t],f[r-(1<<t)+1][t]);
}
inline void calc(int x,int y)
{

    x=(x+1)>>1;y>>=1;
    int pos=rank[x-1],len=y-x+1;
    int l,r,mid,p1=pos,p2=pos;
    l=1;r=pos;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (rmq(mid+1,pos)>=len) p1=mid,r=mid-1;
        else l=mid+1;
    }
    l=pos;r=n;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (rmq(pos+1,mid)>=len) p2=mid,l=mid+1;
        else r=mid-1;
    }
    ans=max(ans,(ll)len*(p2-p1+1));
}
inline void manacher()
{
    int mx=0,id=0;
    F(i,1,n)
    {
        if (mx>i) p[i]=min(p[id*2-i],mx-i+1);
        else p[i]=1;
        while (s[i+p[i]]==s[i-p[i]])
        {
            if (i+p[i]>mx) calc(i-p[i],i+p[i]);
            p[i]++;
        }
        if (i+p[i]-1>mx) mx=i+p[i]-1,id=i;

    }
}
int main()
{
     scanf("%s",s);
         n=strlen(s);
     for(int i=0;i<n;i++)  
         r[i]=s[i]-'a'+1; 
     r[n]=0;
    SA(n+1,28);
    get_H(n);

    F(i,2,n) lg2[i]=lg2[i>>1]+1;
    F(i,1,n) f[i][0]=h[i];


    for(int j=1;(1<<j)<=n;j++) F(i,1,n-(1<<j)+1) f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    s[0]='$';s[1]='#';s[(n+1)<<1]='%';
    F(i,1,n) s[i<<1]=r[i-1]+'a'-1,s[i<<1|1]='#';
    n=n<<1|1;

    manacher();
    printf("%lld\n",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值