[APIO2014] [Uoj103] [Bzoj3676] Palindromes回文串 [Manacher,后缀数组]

用Manacher算法枚举回文子串,每次在后缀数组排序后的后缀数组中二分,因为用某一后缀和其他子串分别求匹配的长度,匹配长度在排序后该后缀的两侧具有单调性(匹配长度为min{H[x]|i<=x<=j},所以对于查询min(H[x])用ST表O(n)预处理,O(1)查询即可。Manacher时间复杂度O(n),后缀数组复杂度O(nlogn),总复杂度O(nlogn)。注意二分时的边界条件!

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>

using namespace std;

long long    Ans;
int    A[310000],B[310000],U[310000];
int    SA[310000],*Rank,H[310000],Tmp[310000];
int    lg2[310000],ST[310000][20],p[610000];
char    str[610000];

void    Get_H(const int n)
{
    int    i,j,k=0;
    for(i=0;i<n;H[Rank[i++]]=k)
        for(k?k--:k,j=SA[Rank[i]-1];str[i+k]==str[j+k];++k);
    for(i=2;i<=n;++i)lg2[i]=lg2[i>>1]+1;
    for(i=1;i<=n;++i)ST[i][0]=H[i];
    for(j=1;(1<<j)<=n;++j)
    {
        for(i=1;i+(1<<j)-1<=n;++i)
        {
            ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
        }
    }
    return ;
}

int    Query(const int l,const int r)
{
    int    temp=lg2[r-l+1];
    return min(ST[l][temp],ST[r-(1<<temp)+1][temp]);
}

bool    cmp(const int * s,const int a,const int b,const int l)
{
    return s[a]==s[b] && s[a+l]==s[b+l];
}

int*    Get_SA(const int n,int    m)
{
    int    i,j,_p,*x=A,*y=B;
    for(i=0;i<m;++i)U[i]=0;
    for(i=0;i<n;++i)U[x[i]=str[i]]++;
    for(i=1;i<m;++i)U[i]+=U[i-1];
    for(i=n-1;i>=0;--i)SA[--U[x[i]]]=i;

    for(j=1,_p=1;_p<n;m=_p,j<<=1)
    {
        for(_p=0,i=n-j;i<n;++i)y[_p++]=i;
        for(i=0;i<n;++i)if(SA[i]>=j)y[_p++]=SA[i]-j;
        for(i=0;i<n;++i)Tmp[i]=x[y[i]];
        for(i=0;i<m;++i)U[i]=0;
        for(i=0;i<n;++i)U[Tmp[i]]++;
        for(i=1;i<m;++i)U[i]+=U[i-1];
        for(i=n-1;i>=0;--i)SA[--U[Tmp[i]]]=y[i];
        for(swap(x,y),_p=1,x[SA[0]]=0,i=1;i<n;++i)
            x[SA[i]]=cmp(y,SA[i-1],SA[i],j)?_p-1:_p++;
    }
    return x;
}

long long    Calc(int l,int r,const int n)
{
    l=(l-1)>>1,r=(r-2)>>1;
    int    pos=Rank[l],L,R,temp=1;

    L=0,R=pos;

    while(L<R-1)
    {
        int    mid=L+((R-L)>>1);
        if(Query(mid+1,pos)>=r-l+1)R=mid;
        else    L=mid;
    }

    temp+=pos-R;
    L=pos,R=n+1;

    while(L<R-1)
    {
        int    mid=L+((R-L)>>1);
        if(Query(pos+1,mid)>=r-l+1)L=mid;
        else    R=mid;
    }
    temp+=L-pos;
    return (long long)temp*(r-l+1);
}

void    Manacher(const int n)
{
    int    i,pos=0;
    for(i=n-1;i>=0;--i)
    {
        str[i+i+2]=str[i];
        str[i+i+1]=‘#‘;
    }
    str[0]=‘^‘;str[n<<1|1]=‘#‘;str[(n+1)<<1]=‘$‘;
    for(i=1;i<=(n<<1|1);++i)
    {
        if(p[pos]+pos>i)
            p[i]=min(p[(pos<<1)-i],p[pos]+pos-i);
        else    p[i]=1;
        while(str[i-p[i]]==str[i+p[i]])
        { if(i+p[i]>p[pos]+pos) Ans=max(Ans,Calc(i-p[i],i+p[i],n)); p[i]++; }
        if(pos+p[pos]<i+p[i])pos=i;
    }

    return ;
}

int main()
{
    int    n;

    scanf("%s",str);

    n=strlen(str);
    Rank=Get_SA(n+1,256);
    Get_H(n);
    Manacher(n);

    printf("%lld",Ans);

    return 0;
}
时间: 2024-12-21 20:18:46

[APIO2014] [Uoj103] [Bzoj3676] Palindromes回文串 [Manacher,后缀数组]的相关文章

(回文串 Manacher )Girls&#39; research -- hdu -- 3294

http://acm.hdu.edu.cn/showproblem.php?pid=3294 Girls' research Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 3294 Description One day, sailormoon girls are so delighted that they intend to res

[BZOJ3676][APIO2014]回文串(Manacher+SAM)

代码总用时:3h 很简单的一道题,只要意识到Manacher算法的本质(本质不同的回文串的个数是O(n)的),配合后缀自动机或者后缀数组就可以轻松解决. 但这道题调了好久,浪费了很多时间,一是因为后缀自动机模板不熟练,而是Manacher算法流程没有一个清楚的认识. 写代码的时候精力要高度集中,不能因为低级错误耽误时间. 下面是SAM版本的代码: #include<cstdio> #include<cstring> #include<algorithm> #define

Hdu 5340 Three Palindromes 最大回文串 Manacher

Three Palindromes Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 80    Accepted Submission(s): 21 Problem Description Can we divided a given string S into three nonempty palindromes? Input Fir

[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∣ 的子串是

最长回文---hdu3068 (回文串 manacher 算法模板)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 题意很清楚:就是求一个串s的子串中最长回文串的长度:这类题用到了manacher算法 manacher算法(复制大神的解释): 定义数组p[i]表示以i为中心的(包含i这个字符)回文串半径长 将字符串s从前扫到后for(int i=0;i<strlen(s);++i)来计算p[i],则最大的p[i]就是最长回文串长度,则问题是如何去求p[i]? 由于s是从前扫到后的,所以需要计算p[i]时一定

【BZOJ2565】最长双回文串 Manacher

题解: 首先我们写一个Manacher模板.. 然后我们可以把所有回文串的信息映射到左端点上, 每个点依此维护最长右连接回文串. 然后再顺着扫一遍就出解了. 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 using namespace std; char ts[N],s[N<<1]; int n,

37:密码截取(回文串manacher算法)

题目描述:Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解.比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 .因为截获的串太长了,而且存在多种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的有效密码

BZOJ 2565 回文串-Manacher

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2565 题意:中文题 思路:定义L[i],R[i].表示以i为左端点/右端点时,最长回文串长度.那么答案就是L[i]+R[i]的最大值.问题转化为怎么求L[i],R[i].我们通过用Manacher可以求出以i为中心的最长回文串半径.然后再通过暴力+剪枝的方法对于每一个i和对应的最长半径求更新L[i],R[i]. #include<iostream> #include<cstdio

POJ 3974 回文串-Manacher

题目链接:http://poj.org/problem?id=3974 题意:求出给定字符串的最长回文串长度. 思路:裸的Manacher模板题. #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> using namespace std; const int MAXN=10