Manacher算法求解回文字符串
Manacher算法可以在\(O(N)\)时间内求解出一个字符串的所有回文子串(正反遍历相同的字串)。
注:回文串显然有两种,一种是奇数长度,如abczcba
,有一个中心字符z
;另外一种是偶数个长度,如abccba
,没有中心字符,下面提到暂时都是只查找奇数长度的字符串
要理解Manacher算法,首先假象一个随机生成的字符串,枚举每个字符作为中心,向两边不断拓展,判断是否相等,直到两边不相等或者走到边界为止,就可以得到每个字符为中心的最大回文长度了(记作\(f_i\)),显然第\(i\)个字符加上左右\(f_i\)的字符就可以构成\(i\)为中心的最长回文串\(S_{[i-f_i+1, i+f_i-1]}\),而\(i\)为中心的更短的字串,显然也是回文串了,这样就求出了所有回文串。
void BF(char *s, int len, int *f) {
for (int i = 1; i <= len; i++) {
f[i] = 1;
while (s[i+f[i]] == s[i-f[i]]) ++f[i];
}
}
但是如果字符大量重叠(如abababab...
),几乎每次拓展都会拓展到最边界,效率就会达到平方级。解决这个问题,就应该想到利用回文串的性质,利用已经得到的\(f_i\)来推出当前的\(f_i\)。
那么回文串有什么性质?首先是对称下标构成的字串肯定对称(比如abczcba
,\(S_{[1, 3]}=S_{[7, 4]}\)),而且回文串的对称串依然是自己(定义)。所以,一个回文串中如果有另一个回文串,那么子串的对称下标肯定也构成了一个相同的回文串,可以直接推出来。换言之,如果我们知道一个回文串内左半边的对称串,就可以直接得到他右半边的对称串
所以我们记录下达到过的最靠右的点和他的中心点(记为\(maxR\)和\(mid\),当然你也可以只记录\(mid\),用\(mid+f[mid]\)表示右边界)。只要枚举的\(i\)还在右边界以内,就尝试用\(f[mid*2-i]\)来更新\(f[i]\)。当然如果左半边回文串的最长左边界\(i-f[i]+1\)已经不在\(mid\)的回文范围内了,那就顶到最左边,把\(f[i]\)更新为\(f[mid]+mid-i\)(也就是最右边\(maxR\)处)。这时就需要继续往后更新,将\(maxR\)往右拓展。
这里是一份参考代码,这里\(maxR\)是开区间。和上面的暴力一样没有做边界特判,应该将\(S[0]和S[len+1]\)设为两个原字符串中没有且不相等的字符(否则可能加上两端,多一个回文串,然后再往外走导致越界)。显然\(f[i]\)取\(f[mid]+mid-i\)时才会执行之后的更新。
void Manacher(char *s, int len, int *f) {
static int maxR, mid;
for (int i = 1; i <= len; i++) {
f[i] = (i < maxR) ? min(f[mid*2-i], f[mid]+mid-i) : 1;
while (s[i+f[i]] == s[i-f[i]]) ++f[i];
if (i + f[i] > maxR)
maxR = (mid = i) + f[i];
}
}
那么为什么只需要这一个优化就可以做到线性呢?因为这样已经可以做到每个字符只被访问一次(算上和后面的字符比较相等就是两次)。回看第一张图,\(maxR\)左侧是已经访问过的,右侧是没有访问过的。这些已经访问过但\(i\)还没有遍历到的位置都可以\(O(1)\)求解,而\(maxR\)只会不断向右移动,因此一定是\(O(N)\)的。其本质就是利用回文串的性质避免了\(mid\)到\(maxR\)处的所有计算,只在往后更新的时候计算。
最后谈谈偶数长度回文串,偶数长度串的“中心”相当于于是字符之间的空隙。处理它们的一种方法是在每两个字符串之间加入不存在于原字符串的字符(如#),然后在执行算法,此时以#为中心的回文串就是偶数回文串。或者先做奇数长度,然后找到所有满足\(S[i]==S[i+1]\)的下标,将他们看作“一个中心“后进行拓展。
for (int i = 1; i <= len; i++)
s0[(i << 1) - 1] = s[i], s0[i << 1] = '#';
Manacher算法求解回文字符串的更多相关文章
- Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version)(Manacher算法+输出回文字符串)
This is the hard version of the problem. The difference is the constraint on the sum of lengths of s ...
- SPOJ STC02 - Antisymmetry(Manacher算法求回文串数)
http://www.spoj.com/problems/STC02/en/ 题意:给出一个长度为n的字符串,问其中有多少个子串s可以使得s = s按位取反+翻转. 例如样例:11001011. 10 ...
- hdu5340—Three Palindromes—(Manacher算法)——回文子串
Three Palindromes Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- HDU 5371(2015多校7)-Hotaru's problem(Manacher算法求回文串)
题目地址:HDU 5371 题意:给你一个具有n个元素的整数序列,问你是否存在这样一个子序列.该子序列分为三部分,第一部分与第三部分同样,第一部分与第二部分对称.假设存在求最长的符合这样的条件的序列. ...
- Manacher算法求回文半径
http://wenku.baidu.com/link?url=WFI8QEEfzxng9jGCmWHoKn0JBuHNfhZ-tKTDMux34CeY8UNUwLVPeY5HA3TyoKU2XegX ...
- Manacher算法:求解最长回文字符串,时间复杂度为O(N)
原文转载自:http://blog.csdn.net/yzl_rex/article/details/7908259 回文串定义:"回文串"是一个正读和反读都一样的字符串,比如&q ...
- 最长回文字符串(manacher算法)
偶然看见了人家的博客发现这么一个问题,研究了一下午, 才发现其中的奥妙.Stupid. 题目描述: 回文串就是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串. ...
- 第5题 查找字符串中的最长回文字符串---Manacher算法
转载:https://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一 ...
- hdu3068 求一个字符串中最长回文字符串的长度 Manacher算法
最长回文 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
随机推荐
- HTTP状态码一览表
常见Http状态码大全 2018年03月16日 11:36:31 阅读数:153 一些常见的状态码为: 200 - 服务器成功返回网页404 - 请求的网页不存在503 - 服务不可用详细分解: 1x ...
- rinted端口转发
https://www.cnblogs.com/linuxk/p/10075803.html 阿里云Redis外网转发访问 1.前提条件 如果您需要从本地 PC 端访问 Redis 实例进行数据操 ...
- ligerUI问题
1.checkboxColWidth:990,Grid的复选框的宽度设置为什么不起作用. 2.当grid出现横线不对齐时,可以设置detailColWidth:90,属性进行设置.此属性好像只是针对复 ...
- 【LeetCode】1151. Minimum Swaps to Group All 1's Together 解题报告 (C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 滑动窗口 日期 题目地址:https://leetco ...
- LeetCode1238循环码排列
题目 给你两个整数 n 和 start.你的任务是返回任意 (0,1,2,,...,2n-1) 的排列 p,并且满足: p[0] = start p[i] 和 p[i+1] 的二进制表示形式只有一位不 ...
- bugku的一道XFF转发代理服务器题 “本地服务器”
X-Forwarded-For requests包内构造方式: X-Forwarded-For: client1, proxy1, proxy2
- 简单的 for 循环也会踩的坑
前言 最近实现某个业务时,需要读取数据然后再异步处理:在 Go 中实现起来自然就比较简单,伪代码如下: list := []*Demo{{"a"}, {"b"} ...
- CS5211替代PS8625|设计DP转LVDS转接板|替代PS8625方案
1.CS5211与PS8625功能概述 CS5211是一个eDP到LVDS转换器,配置灵活,适用于低成本显示系统.CS5211与eDP 1.2兼容,支持1通道和2通道模式,每通道速度为1.62Gbps ...
- 数据可视化 gojs 简单使用介绍
目录 1. gojs 简介 2. gojs 应用场景 3. 为什么选用 gojs: 4. gojs 上手指南 5. 小技巧(非常实用哦) 6. 实践:实现节点分组关系可视化交互图 最后 本文是关于如何 ...
- <数据结构>XDOJ317.输出完全二叉树的某一层
问题与解答 问题描述 对一棵完全二叉树,输出某一深度的所有节点,有则输出这些节点,无则输出EMPTY. 输入格式 输入有多组数据. 每组数据第一行输入一个结点数n(1<=n<=1000), ...