【转载】Manacher算法
本文原创:http://www.cnblogs.com/BigBallon/p/3816890.html
只为了记录学习,不为抄袭!
http://www.felix021.com/blog/read.php?2040
对于Manacher算法,主要的作用是用来求一个字符串的最长回文子串。
这个算法的时间复杂度书线性的,即O(n)
下面我分两个部分来讲
1)预处理
这个算法的精妙之处在于巧妙地避免了考虑回文子串的长度是奇数还是偶数(如果你还不知道什么是回文数,回文串,请自行baidu)
在Manacher算法中,需要提前预处理我们原本的字符串,这里把原串叫做s1, 把预处理之后的字符串叫做s2.
那么,对于s1 = "ababba",
预处理后的s2 = "$#a#b#a#b#b#a#".
这样一来,所有的回文串,在s2中,都是奇数了,你可以自己画一画或者看一看。这里注意一点,我们将s2[0] = '$',也就是一个字符串中没有出现的其他字符,为的是避免访问数组时越界。如果没有这个处理。在代码中的P[i] = min(P[2*id]-i),mx-i)处可能会出现这种情况:
当进行到第二次循环,此时i=1,id = 0,2*id-i = -1,就出现了数组越界。。所以这个处理是必须的。 那么预处理代码大概是这样的:

void init()
{
int i, j = 2;
s2[0] = '$', s2[1] = '#'; for(i=0;s1[i];i++)
{
s2[j++] = s1[i];
s2[j++] = '#';
}
s2[j] = '\0';
}

2)Manacher算法 预处理完成之后,就可以用Manacher算法来求最长回文子串了。
你会发现,所有和字符串相关的算法都很注重利用之前匹配过程中留下的有用信息,当然这个算法也不能例外。 首先,这里有一个辅助数组P[](针对s2而言的),记录的是在P[i]处,包括P[i]本身向左向右延伸的最大字符数。仔细想想你会明白P[i]-1表示的正是原串中对应位置的最回文子串的长度。

//引用前面文章里面的内容
下面以字符串12212321为例
经过上一步,变成了 S[] = "$#1#2#2#1#2#3#2#1#";
然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i],也就是把该回文串“对折”以后的长度),比如S和P的对应关系:
---------------------------------------------------------
S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
---------------------------------------------------------
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)

再来看怎么匹配以及利用有用信息,这里我们用一个变量 id 记录扩张长度位置信息, 用变量 mx 记录id位置处的扩张长度。
先给出代码,结合代码来讲

void manacher(char* s)
{
int i, id = 0, mx = 0;
P[0] = 0; //P[0]位置没用
for(i=1;s[i];i++) //对串进行线性扫描
{
if(mx > i) //如果mx比当前i大,分为两种情况,详细致请看文章开头推荐的blog上的图示,非常给力的图
P[i] = min(P[2*id-i],mx-i);
else //如果mx比i小,没有可以利用的信息,那么就只能从头开始匹配
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]]) P[i]++; //匹配
if(mx < P[i] + i) //坚持是否有更新mx以及id
{
mx = P[i] + i;
id = i;
}
}
}

然后是几道题目:
1. HDU 3068 最长回文
题意:中文的,没什么好说的。模板题目,用来学习非常好
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 222222
#define CLR(a,b) memset(a,b,sizeof(a))
int p[MAXN];
char s1[MAXN],s2[MAXN];
void manacher(char* s)
{
int i, id = 0, mx = 0;
p[0] = 0;
for(int i=1;s[i]!='\0';i++)
{
if(mx > i)
p[i] = min(p[2*id-i],mx-i);
else
p[i] = 1;
while(s[i + p[i]] == s[i - p[i]]) p[i]++;
if(i + p[i] > mx)
{
mx = i + p[i];
id = i;
}
}
}
void init(char* s1,char*s2)
{
s2[0] = '$';
int i = 0;
int j = 1;
for(i=0;s1[i]!='\0';i++)
{
s2[j++] = '#';
s2[j++] = s1[i];
}
s2[j++] = '#';
s2[j] = '\0';
}
void getAC()
{
int res = 0;
for(int i=0;s2[i]!='\0';i++)
res = max(res,p[i]);
printf("%d\n",res-1);
}
int main()
{
while(~scanf("%s",s1))
{
init(s1,s2);
manacher(s2);
getAC();
}
return 0;
}

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 222222
#define CLR(a,b) memset(a,b,sizeof(a))
int p[MAXN];
char s1[MAXN],s2[MAXN]; void manacher(char* s)
{
int i, id = 0, mx = 0;
p[0] = 0;
for(int i=1;s[i]!='\0';i++)
{
if(mx > i)
p[i] = min(p[2*id-i],mx-i);
else
p[i] = 1;
while(s[i + p[i]] == s[i - p[i]]) p[i]++;
if(i + p[i] > mx)
{
mx = i + p[i];
id = i;
}
}
} void init(char* s1,char*s2)
{
s2[0] = '$';
int i = 0;
int j = 1;
for(i=0;s1[i]!='\0';i++)
{
s2[j++] = '#';
s2[j++] = s1[i];
}
s2[j++] = '#';
s2[j] = '\0';
} void getAC()
{
int res = 0;
for(int i=0;s2[i]!='\0';i++)
res = max(res,p[i]); printf("%d\n",res-1);
} int main()
{
while(~scanf("%s",s1))
{
init(s1,s2);
manacher(s2);
getAC();
}
return 0;
}

2. HDU 3294 Girls' research
题意:求最长回文子串,并求出起始位置。
分析:本来就要预处理,这里多了一个预处理,一点也不难,反正都要处理。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define CLR(a,b) memset(a,b,sizeof(a))
#define MAXN 200020
int P[MAXN<<1];
char s1[MAXN], s2[MAXN<<1];
int sta,end,res,id,ch;
void manacher(char* s)
{
int i, id = 0, mx = 0;
P[0] = 0;
for(i=1;s[i];i++)
{
if(mx > i)
P[i] = min(P[2*id-i],mx-i);
else
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]]) P[i]++;
if(mx < P[i] + i)
{
mx = P[i] + i;
id = i;
}
}
}
void init()
{
int i = 0, j, key = 'a' - ch;
for(i=0;s1[i];i++)
{
s1[i] += key;
if(s1[i] > 'z')
s1[i] = s1[i] - 'z' + 'a' - 1;
if(s1[i] < 'a')
s1[i] = 'z' - ('a' - s1[i]) + 1;
}
s2[0] = '$', s2[1] = '#';
for(i=0,j=2;s1[i];i++)
{
s2[j++] = s1[i];
s2[j++] = '#';
}
s2[j] = '\0';
}
void AC()
{
getchar();
init();
manacher(s2);
res = 0, id = 0;
for(int i=0;s2[i];i++)
{
if(res < P[i])
{
res = P[i];
id = i;
}
}
if(res < 3)
puts("No solution!");
else
{
// printf("id = %d\n",id);
// printf("res = %d\n",res);
sta = ((id - res+2)>>1) - 1;
end = sta + res - 2;
printf("%d %d\n",sta,end);
// printf("s1= %s, s2 = %s\n",s1,s2);
for(int i=sta;i<=end;i++)
printf("%c",s1[i]);
puts("");
}
}
int main()
{
while(~scanf("%c %s",&ch,s1))
{
AC();
}
return 0;
}

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define CLR(a,b) memset(a,b,sizeof(a))
#define MAXN 200020
int P[MAXN<<1];
char s1[MAXN], s2[MAXN<<1];
int sta,end,res,id,ch; void manacher(char* s)
{
int i, id = 0, mx = 0;
P[0] = 0;
for(i=1;s[i];i++)
{
if(mx > i)
P[i] = min(P[2*id-i],mx-i);
else
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]]) P[i]++;
if(mx < P[i] + i)
{
mx = P[i] + i;
id = i;
}
}
} void init()
{
int i = 0, j, key = 'a' - ch;
for(i=0;s1[i];i++)
{
s1[i] += key;
if(s1[i] > 'z')
s1[i] = s1[i] - 'z' + 'a' - 1;
if(s1[i] < 'a')
s1[i] = 'z' - ('a' - s1[i]) + 1;
}
s2[0] = '$', s2[1] = '#';
for(i=0,j=2;s1[i];i++)
{
s2[j++] = s1[i];
s2[j++] = '#';
}
s2[j] = '\0';
} void AC()
{
getchar();
init();
manacher(s2);
res = 0, id = 0;
for(int i=0;s2[i];i++)
{
if(res < P[i])
{
res = P[i];
id = i;
}
}
if(res < 3)
puts("No solution!");
else
{
// printf("id = %d\n",id);
// printf("res = %d\n",res);
sta = ((id - res+2)>>1) - 1;
end = sta + res - 2;
printf("%d %d\n",sta,end);
// printf("s1= %s, s2 = %s\n",s1,s2);
for(int i=sta;i<=end;i++)
printf("%c",s1[i]);
puts("");
} } int main()
{
while(~scanf("%c %s",&ch,s1))
{
AC();
} return 0;
}

3. POJ 3974 Palindrome
题意:同HDU3068
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define CLR(a,b) memset(a,b,sizeof(a))
#define MAXN 1111111
int P[MAXN<<1], cas = 1;
char s1[MAXN], s2[MAXN<<1];
void manacher(char* s)
{
int mx = 0, i, id = 0;
P[0] = 0;
for(i=1;s[i];i++)
{
if(mx > i)
P[i] = min(P[2*id-i],mx-i);
else
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]]) P[i]++;
if(mx < P[i] + i)
{
mx = P[i] + i;
id = i;
}
}
}
void init()
{
int i, j = 2;
s2[0] = '$', s2[1] = '#';
for(i=0;s1[i];i++)
{
s2[j++] = s1[i];
s2[j++] = '#';
}
s2[j] = '\0';
}
void AC()
{
printf("Case %d: ",cas++);
int res = 0, i = 0;
for(i=0;s2[i];i++)
{
if(res < P[i])
res = P[i];
}
printf("%d\n",res-1);
}
int main()
{
while(~scanf("%s",s1))
{
if(strcmp("END",s1) == 0)
break;
init();
manacher(s2);
AC();
}
return 0;
}

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; #define CLR(a,b) memset(a,b,sizeof(a))
#define MAXN 1111111
int P[MAXN<<1], cas = 1;
char s1[MAXN], s2[MAXN<<1]; void manacher(char* s)
{
int mx = 0, i, id = 0;
P[0] = 0;
for(i=1;s[i];i++)
{
if(mx > i)
P[i] = min(P[2*id-i],mx-i);
else
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]]) P[i]++;
if(mx < P[i] + i)
{
mx = P[i] + i;
id = i;
}
}
} void init()
{
int i, j = 2;
s2[0] = '$', s2[1] = '#'; for(i=0;s1[i];i++)
{
s2[j++] = s1[i];
s2[j++] = '#';
}
s2[j] = '\0';
} void AC()
{
printf("Case %d: ",cas++);
int res = 0, i = 0;
for(i=0;s2[i];i++)
{
if(res < P[i])
res = P[i];
}
printf("%d\n",res-1);
} int main()
{
while(~scanf("%s",s1))
{
if(strcmp("END",s1) == 0)
break;
init();
manacher(s2);
AC();
}
return 0;
}

4. HDU 4513 吉哥系列故事——完美队形II
题意:求最长回文子串,而外要求:从回文串最中间向两边满足非递增。
分析:一开始不知道怎么解决,后来想想在manacher函数中加一个判断,然后就A了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 100111
int P[MAXN<<1], s1[MAXN], s2[MAXN<<1];
int t,n;
void manacher(int* s)
{
int id = 0, i, mx = 0;
P[0] = 0;
for(i=1;i<2*n+1;i++)
{
if(mx > i)
P[i] = min(P[2*id-i],mx-i);
else
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]])
{
if(s[i + P[i]] != -222)
{
if(s[i + P[i]] <= s[i + P[i] - 2])
P[i]++;
else
break;
}
P[i]++;
}
if(mx < i + P[i])
{
id = i;
mx = P[i] + i;
}
}
}
void init()
{
s2[0] = -111, s2[1] = -222;
int i = 0,j = 2;
for(i=0;i<n;i++)
{
s2[j++] = s1[i];
s2[j++] = -222;
}
}
void AC()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&s1[i]);
init();
manacher(s2);
int ret = -1;
for(int i=0;i<2*n+1;i++)
{
if(P[i] > ret)
ret = P[i];
}
printf("%d\n",ret - 1);
}
int main()
{
scanf("%d",&t);
while(t--)
{
AC();
}
}

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 100111
int P[MAXN<<1], s1[MAXN], s2[MAXN<<1];
int t,n;
void manacher(int* s)
{
int id = 0, i, mx = 0;
P[0] = 0;
for(i=1;i<2*n+1;i++)
{
if(mx > i)
P[i] = min(P[2*id-i],mx-i);
else
P[i] = 1;
while(s[i + P[i]] == s[i - P[i]])
{
if(s[i + P[i]] != -222)
{
if(s[i + P[i]] <= s[i + P[i] - 2])
P[i]++;
else
break;
}
P[i]++;
}
if(mx < i + P[i])
{
id = i;
mx = P[i] + i;
}
}
} void init()
{
s2[0] = -111, s2[1] = -222;
int i = 0,j = 2;
for(i=0;i<n;i++)
{
s2[j++] = s1[i];
s2[j++] = -222;
}
} void AC()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&s1[i]);
init();
manacher(s2);
int ret = -1;
for(int i=0;i<2*n+1;i++)
{
if(P[i] > ret)
ret = P[i];
}
printf("%d\n",ret - 1);
} int main()
{
scanf("%d",&t);
while(t--)
{
AC();
}
}

【转载】Manacher算法的更多相关文章
- manacher算法(转载)
原网址:http://blog.sina.com.cn/s/blog_70811e1a01014esn.html manacher算法是我在网上无意中找到的,主要是用来求某个字符串的最长回文子串.不过 ...
- POJ3974 Palindrome (manacher算法)
题目大意就是说在给定的字符串里找出一个长度最大的回文子串. 才开始接触到manacher,不过这个算法的确很强大,这里转载了一篇有关manacher算法的讲解,可以去看看:地址 神器: #includ ...
- [转] Manacher算法详解
转载自: http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串 ...
- 第5题 查找字符串中的最长回文字符串---Manacher算法
转载:https://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一 ...
- Manacher算法:求解最长回文字符串,时间复杂度为O(N)
原文转载自:http://blog.csdn.net/yzl_rex/article/details/7908259 回文串定义:"回文串"是一个正读和反读都一样的字符串,比如&q ...
- Manacher算法(马拉车算法)浅谈
什么是Manacher算法? 转载自百度百科 Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 O(n) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍 ...
- Manacher算法--O(n)回文子串算法
转自:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824 O(n)回文子串算法 注:转载的这篇文章,我发现下面那个源代码有点bug... ...
- Hash 算法与 Manacher 算法
目录 前言 简单介绍 简述 Hash 冲突 离散化 基本结构 普通 Hash 简述 例题 字符串 Hash 简单介绍 核心思想 基本运算 二维字符串 Hash 例题 兔子与兔子 回文子串的最大长度 后 ...
- HDU3068 回文串 Manacher算法
好久没有刷题了,虽然参加过ACM,但是始终没有融会贯通,没有学个彻底.我干啥都是半吊子,一瓶子不满半瓶子晃荡. 就连简单的Manacher算法我也没有刷过,常常为岁月蹉跎而感到后悔. 问题描述 给定一 ...
随机推荐
- 在mipsel-linux平台上的编译应用SQLite-3.5.9
sqlite 第一个Alpha版本诞生于2000年5月,是实现了SQL 92标准的一个大子集的嵌入式数据库,其以在一个库中组合了数据库引擎和接口,能将所有数据存储于单个文件中.官方测试表明sqlite ...
- iOS10新增Api详解
1.SiriKit SiriKit的功能非常强大,支持音频.视频.消息发送接收.搜索照片.预订行程.管理锻炼等等.在用到此服务时,siri会发送Intent对象,里面包括用户的请求和各种数据,可以对这 ...
- jquery”ScriptResourceMapping
要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping.”的解决办法. 1.先将aspnet.scri ...
- React学习笔记(三) 组件传值
组件嵌套后,父组件怎么向子组件发送数据呢? 答案是: this.props <script type="text/babel"> var MyFirst = React ...
- 345. Reverse Vowels of a String(C++)
345. Reverse Vowels of a String Write a function that takes a string as input and reverse only the v ...
- (转)应用内存优化之OnLowMemory&OnTrimMemory
1.应用内存onLowMemory& onTrimMemory优化 onLowMemory& onTrimMemory简介:OnLowMemory是Android提供的API,在系统内 ...
- 《玩转shutdown》-linux命令五分钟系列之十三
1 我想立即关机! $shutdown -h now 2 我想立即重启 $shutdown -r now 3 我想在23:30分准时关机 $shutdown -h 23:30 4 我想在15分钟后关机 ...
- tomcat gc问题总结
Java内存泄露监控工具:JVM监控工具介绍 http://developer.51cto.com/art/201203/321431.htm 关于施用full gc频繁的分析及解决 http:/ ...
- sec:authorize 标签 通过不通过权限例子
1. 方式一 <sec:authorize ifAnyGranted="ROLE_A"> <a href="a.jsp"> ...
- hive源代码解析之一hive主函数入口
hive其实做的就是解析一条sql然后形成到mapreduce任务,就是一个代码解释器.hive源代码本身就可以分为ql/metasotre/service/serde 这几块:其中 对于Hive来说 ...