本文主要内容转载自:http://blog.youkuaiyun.com/ggggiqnypgjg/article/details/6645824/
1)以下为转载内容:
这里,我介绍一下O(n)回文串处理的一种方法。Manacher算法.
原文地址:
http://zhuhongcheng.wordpress.com/2009/08/02/a-simple-linear-time-algorithm-for-finding-longest-palindrome-sub-string/
其实原文说得是比较清楚的,只是英文的,我这里写一份中文的吧。
首先:大家都知道什么叫回文串吧,这个算法要解决的就是一个字符串中最长的回文子串有多长。这个算法可以在O(n)的时间复杂度内既线性时间复杂度的情况下,求出以每个字符为中心的最长回文有多长,
这个算法有一个很巧妙的地方,它把奇数的回文串和偶数的回文串统一起来考虑了。这一点一直是在做回文串问题中时比较烦的地方。这个算法还有一个很好的地方就是充分利用了字符匹配的特殊性,避免了大量不必要的重复匹配。
算法大致过程是这样。先在每两个相邻字符中间插入一个分隔符,当然这个分隔符要在原串中没有出现过。一般可以用‘#’分隔。这样就非常巧妙的将奇数长度回文串与偶数长度回文串统一起来考虑了(见下面的一个例子,回文串长度全为奇数了),然后用一个辅助数组P记录以每个字符为中心的最长回文串的信息。P[id]记录的是以字符str[id]为中心的最长回文串,当以str[id]为第一个字符,这个最长回文串向右延伸了P[id]个字符。
原串: w aa bwsw f d
新串: # w# a # a # b# w # s # w # f # d #
辅助数组P: 1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1
这里有一个很好的性质,P[id]-1就是该回文子串在原串中的长度(包括‘#’)。如果这里不是特别清楚,可以自己拿出纸来画一画,自己体会体会。当然这里可能每个人写法不尽相同,不过我想大致思路应该是一样的吧。
好,我们继续。现在的关键问题就在于怎么在O(n)时间复杂度内求出P数组了。只要把这个P数组求出来,最长回文子串就可以直接扫一遍得出来了。
由于这个算法是线性从前往后扫的。那么当我们准备求P[i]的时候,i以前的P[j]我们是已经得到了的。我们用mx记在i之前的回文串中,延伸至最右端的位置。同时用id这个变量记下取得这个最优mx时的id值。(注:为了防止字符比较的时候越界,我在这个加了‘#’的字符串之前还加了另一个特殊字符‘$’,故我的新串下标是从1开始的)
好,到这里,我们可以先贴一份代码了。
void pk()
{
int i;
int mx = 0;
int id;
for(i=1; i<n; i++)
{
if( mx > i )
p[i] = MIN( p[2*id-i], mx-i );
else
p[i] = 1;
for(; str[i+p[i]] == str[i-p[i]]; p[i]++)
;
if( p[i] + i > mx )
{
mx = p[i] + i;
id = i;
}
}
}

2)这篇博客讲解的很透彻,本文只是在其基础上写了一个完整运行的代码。
#include<iostream>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
int main()
{
string old_str="waabwswfd";
int old_len=old_str.length();
//构造新串,首位和每两个相邻字符之间插入一个分隔符,这里用#
int len=2*old_len+2;
string str(len-1,'#');
for(int i=0;i<old_len;++i){
str[2*i+1]=old_str[i];
}
str = '$'+str +='\0';//开始位置插入一个$,防止越界
int max_pos=0;//最长回文串中心位置
//求以每个字符为中心的回文串长度
vector<int> p(len,1);
int mx=0,pos;//mx为以str[pos]为中心时,回文串最远向右延伸到str[mx]
for(int i=1;i<len;++i){
if(mx>i){
p[i]=min(p[2*pos-i],mx-i);
//2*pos-i为i关于pos对称的在pos左面的位置
/*
这个算式基于以下的原理:
(1)若i>j,则当我们计算到p[i]时,p[j]的值我们已经算完。
(2)若以str[i]为中心(用以str[2*pos-i]为中心来替代,因为已经计算过),回文串向右最远扩展了k个字符,那么
(2-1)若str[i]+k<mx,即上一次计算以str[pos]为中心的最长回文串时,已经包含了已str[i]为中心的回文串,则p[i]=p[2*pos-i]
(2-2)若str[i]+k>mx,那么对于str[i]+k大于mx那一部分,我们不确定它是否仍然匹配,所以只能令p[i]=mx-i,因为这段我们知道是回文串。
*/
}
//逐个匹配
while(str[i+p[i]]==str[i-p[i]]){
p[i]++;
}
if(i+p[i]>mx){ //更新向右最远匹配位置
mx=p[i]+i;
pos=i;
}
max_pos=p[i]>p[max_pos]?i:max_pos;
}
//准备求最长回文串
int max_len=p[max_pos]-1;
int start=(max_pos-p[max_pos])/2;
string ans=old_str.substr(start,max_len);
cout<<ans<<endl;
return 0;
}