这题可以用回文自动机来做,但是我并没有学,于是用Manacher+SA的做法O(nlogn)水过

首先,看到回文串就能想到用Manacher

同样还是要利用Manacher能不重复不遗漏地枚举每个回文子串的性质

只是不重复不遗漏还不够,我们还要统计出现次数

每个子串一定是一个后缀的前缀,于是可以用后缀数组

用后缀数组求出height数组之后,对于在Manacher过程中枚举到的每个长度为k的回文串,可以在height数组中二分,用O(logn)的时间求出这个子串的出现次数

BZOJ和COGS上有评论说Manacher + SA的方式被卡了,也有人说自己跑了19s,我这个实现是在BZOJ上跑了10s,COGS的76组数据总共跑了3.7s。

代码如下:

 #include <cstring>
#include <algorithm>
#include <cstdio>
#include <cctype> using namespace std;
typedef long long ll;
const int MAXN = , LOGN = ; int n;
char str[MAXN];
int sas[MAXN], san;
int mas[MAXN<<], man; namespace SA {
int sa[MAXN], rk[MAXN], ht[MAXN];
int tmp1[MAXN], tmp2[MAXN], cnt[MAXN];
int minv[MAXN][LOGN], logn[MAXN];
void solve( int m ) {
int *x = tmp1, *y = tmp2;
for( int i = ; i < m; ++i ) cnt[i] = ;
for( int i = ; i < san; ++i ) ++cnt[ x[i] = sas[i] ];
for( int i = ; i < m; ++i ) cnt[i] += cnt[i-];
for( int i = san-; i >= ; --i ) sa[--cnt[x[i]]] = i;
for( int k = ; k <= san; k <<= ) {
int p = ;
for( int i = san-k; i < san; ++i ) y[p++] = i;
for( int i = ; i < san; ++i ) if( sa[i] >= k ) y[p++] = sa[i]-k;
for( int i = ; i < m; ++i ) cnt[i] = ;
for( int i = ; i < san; ++i ) ++cnt[x[i]];
for( int i = ; i < m; ++i ) cnt[i] += cnt[i-];
for( int i = san-; i >= ; --i ) sa[--cnt[x[y[i]]]] = y[i];
swap(x,y), x[sa[]] = , p = ;
for( int i = ; i < san; ++i )
x[sa[i]] = y[sa[i]] == y[sa[i-]] && y[sa[i]+k] == y[sa[i-]+k] ? p- : p++;
if( p == san ) break;
m = p;
}
for( int i = ; i < san; ++i ) rk[i] = x[i];
int k = ;
for( int i = ; i < san; ++i ) {
if( k ) --k;
if( !rk[i] ) continue;
int j = sa[rk[i]-];
while( sas[i+k] == sas[j+k] ) ++k;
ht[rk[i]] = minv[rk[i]][] = k;
}
for( int k = ; (<<k) <= san; ++k )
for( int i = ; i+(<<k) <= san; ++i )
minv[i][k] = min( minv[i][k-], minv[i+(<<(k-))][k-] );
k = ;
for( int i = ; i <= san; ++i ) {
if( (<<(k+)) <= i ) ++k;
logn[i] = k;
}
}
int qmin( int l, int r ) {
int k = logn[r-l+];
return min( minv[l][k], minv[r+-(<<k)][k] );
}
} void input() {
scanf( "%s", str ), n = strlen(str);
man = ;
for( int i = ; i < n; ++i ) {
sas[i] = str[i];
mas[man++] = '#', mas[man++] = str[i];
}
sas[n] = , san = n+;
mas[man++] = '#';
SA::solve();
} ll ans = ;
int rd[MAXN<<];
void update( int p, int k ) {
using namespace SA;
p = rk[p];
int LL = , LR = p;
while( LL < LR ) {
int mid = (LL+LR)>>;
if( qmin(mid+,p) >= k ) LR = mid;
else LL = mid+;
}
int RL = p, RR = san-;
while( RL < RR ) {
int mid = (RL+RR+)>>;
if( qmin(p+,mid) >= k ) RL = mid;
else RR = mid-;
}
ans = max( ans, ll(k)*(RL-LL+) );
}
int cnt[] = {};
void manacher() {
int mx = , p = ;
for( int i = ; i < man; ++i ) {
if( i < mx ) rd[i] = min( rd[*p-i], mx-i );
else rd[i] = ;
while( i+rd[i] < man && i-rd[i] >= && mas[i+rd[i]] == mas[i-rd[i]] ) {
if( islower( mas[i-rd[i]] ) ) update( (i-rd[i])/, rd[i]+ );
++rd[i];
}
if( i+rd[i] > mx ) mx = i+rd[i], p = i;
}
for( int i = ; i < n; ++i )
ans = max( ans, (ll)++cnt[str[i]-'a'] );
printf( "%lld\n", ans );
} int main() {
// freopen( "apio2014_palindrome.in", "r", stdin );
// freopen( "apio2014_palindrome.out", "w", stdout );
input(), manacher();
return ;
}

【题解】回文串 APIO 2014 BZOJ 3676 COGS 1985 Manacher+后缀数组+二分的更多相关文章

  1. BZOJ 1717 [USACO06DEC] Milk Patterns (后缀数组+二分)

    题目大意:求可重叠的相同子串数量至少是K的子串最长长度 洛谷传送门 依然是后缀数组+二分,先用后缀数组处理出height 每次二分出一个长度x,然后去验证,在排序的后缀串集合里,有没有连续数量多于K个 ...

  2. BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

    二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...

  3. BZOJ 3676 【APIO2014】 回文串

    题目链接:回文串 我终于也会回文自动机辣! 其实吗……我觉得回文自动机(听说这玩意儿叫\(PAM\))还是比较\(simple\)的……至少比\(SAM\)友善多了…… 所谓回文自动机,每个节点就代表 ...

  4. bzoj 2565: 最长双回文串 回文自动机

    题目: Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同 ...

  5. BZOJ2565:最长双回文串——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=2565 题目大意: 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(ab ...

  6. P3649 [APIO2014]回文串(回文树)

    题目描述 给你一个由小写拉丁字母组成的字符串 ss .我们定义 ss 的一个子串的存在值为这个子串在 ss 中出现的次数乘以这个子串的长度. 对于给你的这个字符串 ss ,求所有回文子串中的最大存在值 ...

  7. POJ 3974 回文串-Manacher

    题目链接:http://poj.org/problem?id=3974 题意:求出给定字符串的最长回文串长度. 思路:裸的Manacher模板题. #include<iostream> # ...

  8. manacher算法,求回文串

    用来求字符串最长回文串或者回文串的总数量 #include<map> #include<queue> #include<stack> #include<cma ...

  9. 牛客寒假算法基础集训营4 I Applese 的回文串

    链接:https://ac.nowcoder.com/acm/contest/330/I来源:牛客网 自从 Applese 学会了字符串之后,精通各种字符串算法,比如……判断一个字符串是不是回文串. ...

随机推荐

  1. 【转】: 探索Lua5.2内部实现:虚拟机指令(2) MOVE & LOAD

    name args desc OP_MOVE A B R(A) := R(B) OP_MOVE用来将寄存器B中的值拷贝到寄存器A中.由于Lua是register based vm,大部分的指令都是直接 ...

  2. 【20180808模拟测试】T2 k-斐波那契

    描述 k-斐波拉契数列是这样的 f(0)=k;f(1)=k;f(n)=(f(n-1)+f(n-2))%P(n>=2); 现在我们已经知道了f(n)=1,和P: k的范围是[1,P); 求k的所有 ...

  3. typescript 学习记录

    类型判断: typeJudge() { //typeof 用来判断变量类型 var s: string = 'egret'; var isString: boolean = typeof s === ...

  4. 用 splice 函数分别实现 push、pop、shift、unshift 的方法

    主要需要注意的是不同方法他们本身返回的值应该是什么,是数组当前的长度,还是取出的元素的值,再在splice函数里面进行相应的return就可以了.具体如下: 用 splice函数实现 push方法 f ...

  5. windows编程入门最重要的

    要入门 Windows 编程,最重要的不是阅读什么教材,使用什么工具,而是先必须把以下几个对于初学者来说非常容易困惑的重要概念搞清楚: 1. 文字的编码和字符集.这部分需要掌握 ANSI 模式和 Un ...

  6. 软件工程 作业part3 读后感

    匆匆看完构建之法,觉得这种不认真看完书就去写随笔去评价这本书是对作者的不尊重,所以觉得应该提问题和写感悟. 我的一点拙见,提的问题在现在这个信息发达的时候感觉只要有时间都可以自己解决. 感觉软件工程这 ...

  7. lintcode-186-最多有多少个点在一条直线上

    186-最多有多少个点在一条直线上 给出二维平面上的n个点,求最多有多少点在同一条直线上. 样例 给出4个点:(1, 2), (3, 6), (0, 0), (1, 3). 一条直线上的点最多有3个. ...

  8. iOS-tableView刷新指定行,组

    /一个section刷新 NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:]; [tableview reloadSections:inde ...

  9. 【转】bind简单示例

    bind简单示例代码 namespace { class placeholder_ {}; placeholder_ __1; } template <typename R, typename ...

  10. <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<一>

    <Item 5> Know what functions C++ silently writes and calls 1.If you don't declare them yoursel ...