本文原创: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算法的更多相关文章

  1. manacher算法(转载)

    原网址:http://blog.sina.com.cn/s/blog_70811e1a01014esn.html manacher算法是我在网上无意中找到的,主要是用来求某个字符串的最长回文子串.不过 ...

  2. POJ3974 Palindrome (manacher算法)

    题目大意就是说在给定的字符串里找出一个长度最大的回文子串. 才开始接触到manacher,不过这个算法的确很强大,这里转载了一篇有关manacher算法的讲解,可以去看看:地址 神器: #includ ...

  3. [转] Manacher算法详解

    转载自: http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串 ...

  4. 第5题 查找字符串中的最长回文字符串---Manacher算法

    转载:https://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一 ...

  5. Manacher算法:求解最长回文字符串,时间复杂度为O(N)

    原文转载自:http://blog.csdn.net/yzl_rex/article/details/7908259 回文串定义:"回文串"是一个正读和反读都一样的字符串,比如&q ...

  6. Manacher算法(马拉车算法)浅谈

    什么是Manacher算法? 转载自百度百科 Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 O(n) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍 ...

  7. Manacher算法--O(n)回文子串算法

    转自:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824 O(n)回文子串算法 注:转载的这篇文章,我发现下面那个源代码有点bug... ...

  8. Hash 算法与 Manacher 算法

    目录 前言 简单介绍 简述 Hash 冲突 离散化 基本结构 普通 Hash 简述 例题 字符串 Hash 简单介绍 核心思想 基本运算 二维字符串 Hash 例题 兔子与兔子 回文子串的最大长度 后 ...

  9. HDU3068 回文串 Manacher算法

    好久没有刷题了,虽然参加过ACM,但是始终没有融会贯通,没有学个彻底.我干啥都是半吊子,一瓶子不满半瓶子晃荡. 就连简单的Manacher算法我也没有刷过,常常为岁月蹉跎而感到后悔. 问题描述 给定一 ...

随机推荐

  1. C#字符串颠倒输出

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...

  2. Page.ClientScript.RegisterStartupScript不执行问题

    c#后台使用Page.ClientScript.RegisterStartupScript在前台注册一段脚本提示,发现没有效果,寻寻觅觅,终于从度娘处找到了原因: 该页面多次使用到了Page.Clie ...

  3. JPEG 图

    多媒体教程 - JPEG 图 JPEG 是在 Web 上使用的主要图像格式之一. 本文讲解 JPEG 图像的概念和特性. 理解图像格式 无论是 HTML 还是 XHTML 都没有规定图像的官方格式.然 ...

  4. JavaScript上下文和闭包

    "this" 上下文 上下文通常是取决于一个函数如何被调用.当函数作为对象的方法被调用时,this 被设置为调用方法的对象: var object = { foo: functio ...

  5. nullptr和NULL 区别

    注:本文内容摘自网络,准确性有待验证,现阶段仅供学习参考.尊重作品作者成果,原文链接 :http://www.2cto.com/kf/201302/190008.html 1.为什要有nullptr ...

  6. Windows Phone 之下拉菜单ListPicker

    默认情况下,Visual Studio的ToolBox里没有任何下拉菜单的控件可供使用,虽然可以手工输入代码使用隐藏的ComboBox来实现下拉菜单,但是显示出来的菜单与Metro UI主题不匹配.S ...

  7. demo_04绘制三角形

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. php批量发送短信或邮件的方案

    最近遇到在开发中遇到一个场景,后台管理员批量审核用户时候,需要给用户发送审核通过信息,有人可能会想到用foreach循环发送,一般的短信接口都有调用频率,循环发送,肯定会导致部分信息发送失败,有人说用 ...

  9. [CSS]background背景

    css背景样式 序号  中文说明  标记语法  1  背景颜色  {background-color:数值}  2  背景图片  {background-image: url('imgpath/img ...

  10. RPMForge——Quick Start build system

    How to setup multimedia on CentOS-5 CentOS ships with basic sound support for audio content encoded ...