[APIO2014]回文串 manacher 后缀数组
题面:洛谷
题解:
还是这个性质:对于每个串而言,本质不同的回文串最多只有O(n)个。
所以我们先求出这O(n)个本质不同的回文串,然后对整个串求一次sa。
然后对于每个回文串,求出它的出现次数,更新答案即可。
如何用后缀数组求一个串的出现次数?
因为每个串都必然是某个后缀的前缀。因此我们先找到这个串x,然后在周围二分,寻找一个最大的区间[l, r]使得区间内每个串与x的LCP都大于等于这个串的长度。
可以证明,这样的区间必然是连续的一个。
因此在周围分别向上向下二分一下最多能延伸到哪,是否可行用st表维护一下height数组的最小值即可O(1)查询LCP大小。
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 601000
#define ac 601000
#define LL long long int n, m = , pos, maxn;
LL ans;
int r[ac], sa[AC], rk[AC], p1[AC], p2[AC], b[AC], d[AC];//第几是谁,你是第几, 两个关键字,临时数组,桶
int st[AC][], h[AC], last[AC], length[AC];
char ss[AC], s[ac];//原串,扩充串 inline void upmax(LL &a, LL b){
if(b > a) a = b;
} void pre()
{
scanf("%s", ss + ), n = strlen(ss + );
s[] = '$', s[] = '#';
for(R i = ; i <= n; i ++) s[i << ] = ss[i], s[(i << ) + ] = '#';//manacher数组
for(R i = ; i <= n; i ++) sa[i] = i, rk[i] = ss[i];//初始化后缀数组
} void ssort()
{
for(R i = ; i <= n; i ++) ++ d[p2[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = ; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配排名(临时sa数组)
for(R i = ; i <= m; i ++) d[i] = ; for(R i = ; i <= n; i ++) ++ d[p1[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = n; i; i --) sa[d[p1[b[i]]] --] = b[i];//依次给b[i]分配排名
for(R i = ; i <= m; i ++) d[i] = ;
} void sa_sort()
{
for(R k = ; k <= n; k <<= )
{
for(R i = ; i <= n; i ++)
p1[i] = rk[i], p2[i] = rk[i + k];
ssort();
int tmp = ;
rk[sa[]] = ;
for(R i = ; i <= n; i ++)
rk[sa[i]] = (p1[sa[i]] == p1[sa[i - ]] && p2[sa[i]] == p2[sa[i - ]]) ? tmp : ++ tmp;
if(tmp >= n) break;
m = tmp;
}
} void manacher()//获取回文串
{
int b = * n;
for(R i = ; i <= b; i ++)
{
r[i] = (maxn > i) ? min(r[(pos << ) - i], maxn - i + ) : ;
while(s[i - r[i]] == s[i + r[i]]) ++ r[i];
int t = i + r[i] - ;
if(t > maxn)
{
for(R j = maxn + ; j <= t; ++ j)
{
last[j] = (i << ) - j;//串的开始是要算的,,,,
if(s[j] == '#') continue;
length[j] = ((j - last[j]) >> ) + ;
}
pos = i, maxn = t;
}
} /*for(R i = 1; i <= b; i ++)
{
if(s[i] == '#') continue;
length[i] = ((i - last[i]) >> 1) + 1;//(r - l) / 2 + 1算出字母的长度(个数)
}*/
} void build()//求height数组
{
//h[sa[1]] = 0;
for(R i = , k = ; i <= n; i ++)//按照原串顺序求h
{
if(k) -- k;//将k变为上一次的h[i] - 1
int j = sa[rk[i] - ];
while(ss[i + k] == ss[j + k] && k <= n) ++ k;
h[rk[i]] = k;
}
} int t1[AC], t2[AC];//最接近i的2的n次幂的指数,对应的大小 void get_st()
{
int tt = , tmp = ;
for(R i = ; i <= n; i ++)
{
st[i][] = h[i];
if(i == (tmp << )) tmp <<= , ++ tt;
t1[i] = tt, t2[i] = tmp;
}
tmp = ;
for(R i = ; i <= ; i ++)
{
for(R j = ; j <= n; j ++)
st[j][i] = min(st[j][i - ], st[j + tmp][i - ]);
tmp <<= ;
}
} bool check(int l, int r, int lim)//看min[l, r]是否>= lim
{
int rnt = , len = (r - l + );
rnt = min(st[l][t1[len]], st[r - t2[len] + ][t1[len]]);
return rnt >= lim;
} LL half(int x, int len)//获取出现次数
{
int l = , r = x;
while(l < r)//查找前一段里面第一个符合的
{
int mid = (l + r) >> ;
if(check(mid + , x, len)) r = mid;
else l = mid + ;
}
int ll = l;//记录左边界
l = x, r = n;
while(l < r)//查找后一段里面最后一个符合的
{
int mid = (l + r + ) >> ;
if(check(x + , mid, len)) l = mid;
else r = mid - ;
}
return r - ll + ;
} void work()
{
int b = * n;
for(R i = ; i <= b; i ++)
{
if(s[i] == '#') continue;
upmax(ans, 1LL * length[i] * half(rk[last[i] >> ], length[i]));
}
printf("%lld\n", ans);
} int main()
{
// freopen("in.in", "r", stdin);
pre();
sa_sort();
build();
get_st();
manacher();
work();
// fclose(stdin);
return ;
}
[APIO2014]回文串 manacher 后缀数组的更多相关文章
- [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增
Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ...
- [BZOJ3676][APIO2014]回文串(Manacher+SAM)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3097 Solved: 1408[Submit][Statu ...
- [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3396 Solved: 1568[Submit][Statu ...
- BZOJ4755 [JSOI2016]扭动的回文串 【后缀数组】【manacher】
题目分析: 我写了史上最丑的后缀数组,怎么办? 首先manacher一遍两个串,这样只用考虑第三问.用$作为间隔符拼接两个串,把第一个串翻转.枚举回文中心,取最长的回文串,对于剩下的部分利用LCP匹配 ...
- BZOJ3676 APIO2014 回文串 Manacher、SA
传送门 首先一个结论:串\(S\)中本质不同的回文串个数最多有\(|S|\)个 证明考虑以点\(i\)结尾的所有回文串,假设为\(S[l_1,i],S[l_2,i],...,S[l_k,i]\),其中 ...
- 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)
传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ...
- bzoj 3676: [Apio2014]回文串【后缀自动机+manacher】
用manacher找出本质不同的回文子串放在SAM上跑 #include<iostream> #include<cstdio> #include<cstring> ...
- 【BZOJ 3676】 3676: [Apio2014]回文串 (SAM+Manacher+倍增)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2343 Solved: 1031 Description 考 ...
- BZOJ 3676: [Apio2014]回文串
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2013 Solved: 863[Submit][Status ...
随机推荐
- [WC2010]重建计划 长链剖分
[WC2010]重建计划 LG传送门 又一道长链剖分好题. 这题写点分治的人应该比较多吧,但是我太菜了,只会长链剖分. 如果你还不会长链剖分的基本操作,可以看看我的长链剖分总结. 首先一看求平均值最大 ...
- 使用iChecker的注意事项
1. 要先引用jquery 2. ichecker分好多主题,每个主题带好几种颜色,在配置的时候最好指定一下. 比如引入了square主题的blue颜色演示,配置项中checkboxClass就写ic ...
- 2017"百度之星"程序设计大赛 - 初赛(A) 小C的倍数问题
谢谢帮忙刷访问量! 题解当然下考再发啦 答案为P-1的约数个数 // It is made by XZZ #include<cstdio> #include<algorithm> ...
- unity图形圆形展开
脚本如下: using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngi ...
- Eclipse与MySQL数据库连接步骤
将Eclipse与数据库进行连接的步骤: 1. 下载并配置MySQL 2. 为新建的项目配置mysql的jar包(jdbc和connection的配置) a) 可直接引用外部文件(不建议做,这样项目一 ...
- 利用PreparedStatement预防SQL注入
1.什么是sql注入 SQL 注入是用户利用某些系统没有对输入数据进行充分的检查,从而进行恶意破坏的行为. 例如登录用户名采用 ' or 1=1 or username=‘,后台数据查询语句就变成 ...
- Java泛型理解
Java泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型.当需要使用某一种算法时,又无法具体算法的数据类型,或者想指定类型值的上限或下限,那么这时就需要Java泛型来大显身手 ...
- 基于Mininet测量路径的损耗率
基于Mininet测量路径的损耗率 控制器采用POX,基于OVS仿真 Mininet脚本 创建Node mininet.node Node 创建链路连接 mininet.link TCLink 设置i ...
- thinkphp5框架生成二维码(二)
上篇已经讲过了SDK之类的,这个不再重复,有不知道的童鞋们,请去看上篇文章吧. 这里我用的方法比较老旧,大家有更好的方法,可以进行改良,还有linux服务器,记得给文件权限,否则生成的文件会失败的.大 ...
- USACO 1.2.2 Transformations 方块转换
Description 一块N x N(1<=N<=10)正方形的黑白瓦片的图案要被转换成新的正方形图案.写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式: 1:转90度 ...