[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);
}

你可能感兴趣的:([bzoj 3676][uoj #103]【APIO2014】Palindromes回文串 后缀数组+manachar)