传送门


首先一个结论:串\(S\)中本质不同的回文串个数最多有\(|S|\)个

证明考虑以点\(i\)结尾的所有回文串,假设为\(S[l_1,i],S[l_2,i],...,S[l_k,i]\),其中\(l_1 < l_2 < ... < l_k\),那么因为\(S[l_i,i]\)是个回文串,所以\(S[l_2,i] = S[l_1,l_1 + i - l_2]\),那么这个串可以在以点\(l_1 + i - l_2\)结尾的字符串中被考虑到,当前无需考虑。所以对于以\(i\)结尾的所有串,只有\(S[l_1,i]\)需要考虑。所以以\(i\)结尾的所有回文串至多会生成出一个本质不同的回文串,所以至多有\(|S|\)个本质不同的回文串。

跑一边\(Manacher\)将上面的\(S[l_1,i]\)中的\(l_1\)计算出来,然后在\(SA\)里计算一下与\(suf_{l_1}\)的LCP大于等于\(i - l_1 + 1\)的后缀数量,就是这个串在\(S\)中的出现次数。

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
//This code is written by Itst
using namespace std;

const int MAXN = 3e5 + 7;
char s[MAXN];
int L;
namespace SA{
    int pot[MAXN << 1] , rk[MAXN << 1] , sa[MAXN] , tp[MAXN << 1] , h[MAXN];
    int maxN , logg2[MAXN] , ST[21][MAXN];

    void sort(int p){
        memset(pot , 0 , sizeof(int) * (maxN + 1));
        for(int i = 1 ; i <= L ; ++i)
            ++pot[rk[i]];
        for(int i = 1 ; i <= maxN ; ++i)
            pot[i] += pot[i - 1];
        for(int i = 1 ; i <= L ; ++i)
            sa[++pot[rk[tp[i]] - 1]] = tp[i];
        swap(tp , rk);
        for(int i = 1 ; i <= L ; ++i)
            rk[sa[i]] = rk[sa[i - 1]] + (tp[sa[i]] != tp[sa[i - 1]] || tp[sa[i] + p] != tp[sa[i - 1] + p]);
        maxN = rk[sa[L]];
    }

    void init_ST(){
        for(int i = 2 ; i <= L ; ++i)
            logg2[i] = logg2[i >> 1] + 1;
        for(int i = 2 ; i <= L ; ++i)
            ST[0][i] = h[i];
        for(int i = 1 ; 1 << i <= L - 1 ; ++i)
            for(int j = 2 ; j + (1 << i) - 1 <= L ; ++j)
                ST[i][j] = min(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
    }

    void init(){
        scanf("%s" , s + 1);
        L = strlen(s + 1);
        maxN = 26;
        for(int i = 1 ; i <= L ; ++i)
            rk[tp[i] = i] = s[i] - 'a' + 1;
        sort(0);
        for(int i = 1 ; maxN != L ; i <<= 1){
            int cnt = 0;
            for(int j = 1 ; j <= i ; ++j)
                tp[++cnt] = L - i + j;
            for(int j = 1 ; j <= L ; ++j)
                if(sa[j] > i)
                    tp[++cnt] = sa[j] - i;
            sort(i);
        }
        for(int i = 1 ; i <= L ; ++i){
            if(rk[i] == 1) continue;
            int t = rk[i];
            h[t] = max(0 , h[rk[i - 1]] - 1);
            while(s[sa[t] + h[t]] == s[sa[t - 1] + h[t]])
                ++h[t];
        }
        init_ST();
    }

    int qST(int x , int y){
        if(x > y) x ^= y ^= x ^= y;
        int t = logg2[y - x + 1];
        return min(ST[t][x] , ST[t][y - (1 << t) + 1]);
    }

    long long work(int pos , int len){
        int ansL , l = 1 , r = rk[pos];
        while(l < r){
            int mid = (l + r) >> 1;
            qST(mid + 1 , rk[pos]) >= len ? r = mid : l = mid + 1;
        }
        ansL = l;
        l = rk[pos]; r = L;
        while(l < r){
            int mid = (l + r + 1) >> 1;
            qST(rk[pos] + 1 , mid) >= len ? l = mid : r = mid - 1;
        }
        return 1ll * (r - ansL + 1) * len;
    }
}

namespace manacher{
    int maxL[MAXN << 1] , minL[MAXN];
    char S[MAXN << 1];

    void work(){
        for(int i = 1 ; i <= L ; ++i)
            S[(i << 1) - 1] = s[i];
        int maxR = 0 , maxI = 1;
        for(int i = 1 ; i < (L << 1) ; ++i){
            int l = min(maxL[2 * maxI - i] , maxR - i);//曾经把min写成了max。。。
            while(l <= i && i + l <= (L << 1) && S[i - l] == S[i + l])
                ++l;
            maxL[i] = l;
            if(l + i > maxR){
                maxR = l + i;
                maxI = i;
            }
        }
        memset(minL , 0x3f , sizeof(minL));
        for(int i = 1 ; i < (L << 1) ; ++i)
            minL[(maxL[i] + i - 1) >> 1] = min(minL[(maxL[i] + i - 1) >> 1] , ((i - maxL[i] + 1) >> 1) + 1);
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    SA::init();
    manacher::work();
    long long ans = 0;
    for(int i = 1 ; i <= L ; ++i)
        if(manacher::minL[i] <= i)
            ans = max(ans , SA::work(manacher::minL[i] , i - manacher::minL[i] + 1));
    cout << ans;
    return 0;
}

BZOJ3676 APIO2014 回文串 Manacher、SA的更多相关文章

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

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 3097  Solved: 1408[Submit][Statu ...

  2. [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增

    Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ...

  3. bzoj3676 [Apio2014]回文串 卡常+SAM+树上倍增

    bzoj3676 [Apio2014]回文串 SAM+树上倍增 链接 bzoj luogu 思路 根据manacher可以知道,每次暴力扩展才有可能出现新的回文串. 所以推出本质不同的回文串个数是O( ...

  4. [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串

    回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ...

  5. [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 3396  Solved: 1568[Submit][Statu ...

  6. BZOJ3676 APIO2014回文串(manacher+后缀自动机)

    由于本质不同的回文子串数量是O(n)的,考虑在对于每个回文子串在第一次找到它时对其暴力统计.可以发现manacher时若右端点移动则找到了一个新回文串.注意这样会漏掉串长为1的情况,特判一下. 现在问 ...

  7. BZOJ3676: [Apio2014]回文串(SAM+Manacher/PAM)

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

  8. [APIO2014]回文串 manacher 后缀数组

    题面:洛谷 题解: 还是这个性质:对于每个串而言,本质不同的回文串最多只有O(n)个. 所以我们先求出这O(n)个本质不同的回文串,然后对整个串求一次sa. 然后对于每个回文串,求出它的出现次数,更新 ...

  9. 【回文自动机】bzoj3676 [Apio2014]回文串

    回文自动机讲解!http://blog.csdn.net/u013368721/article/details/42100363 pam上每个点代表本质不同的回文子串.len(i)代表长度,cnt(i ...

随机推荐

  1. 【读书笔记】iOS-iCloud介绍

    iCloud是一种面向消费者市场的云存储服务,苹果公司已经做了大量的工作让用户能够平滑过渡到iCloud,不过对开发者而言这意味着新的负担. 怎样使用iCloud? 你可以使用2种方式在你的应用中使用 ...

  2. Flutter 不一样的跨平台解决方案

    本文主要介绍Flutter相关的东西,包括Fuchsia.Dart.Flutter特性.安装以及整体架构等内容. 1. 简介 Flutter作为谷歌最近推出的跨平台开发框架,一经推出便吸引了不少注意. ...

  3. tensorflow 文件队列

    使用文件队列,防止爆内存 # # 通过队列打开图片文件 file_queue = tf.train.string_input_producer(paths) img_reader = tf.Whole ...

  4. PHP开发APP接口学习笔记

    习要点概述1.APP接口简介 2.封装通信接口方法 3.核心技术 4.APP接口实例 服务器和客户端进行接口数据通信:服务器 -->数据库|缓存 -->调用接口 -->客户端 服务器 ...

  5. 利用PCA进行故障监测

    利用PCA进行故障监测,传统的统计指标有两种:Hotelling-T2和平方预测误差(Squared prediction error, SPE).T2统计量反映了每个主成分在变化趋势和幅值上偏离模型 ...

  6. VB6 对象库未注册问题

    以下是个人使用VB6出现对象库未注册问题的解决方法.已成功! 一.注册ocx文件 mscomctl.ocx文件放进路径C:\Windows\System32(64是此路径,由于本人是64位系统32位未 ...

  7. 3.7Python数据处理篇之Numpy系列(七)---Numpy的统计函数

    目录 目录 前言 (一)函数一览表 (二)统计函数1 (三)统计函数2 目录 前言 具体我们来学Numpy的统计函数 (一)函数一览表 调用方式:np.* .sum(a) 对数组a求和 .mean(a ...

  8. January 28th, 2018 Week 05th Sunday

    I wish you all I ever wanted for you, I wish you the best. 我希望你不负我的期望,愿你一切安好. I hope I can live up t ...

  9. Jersey常用注解解释 @DET、@PUT、@POST 、@DELETE等

    uri : ... /resource/{id} public voide method(@PathParam("id") String userId){} uri :  .../ ...

  10. FileInputStream与FileOutputStream学习笔记

    这是我的第一篇博客,纪念一下吧! 最近学习了IO流,想着学长说的话,就突然想要写写博客了,别管这是什么逻辑了,进入正题. 一.FileInputStream 1.概念 FileInputStream是 ...