class Solution {
public:
string preProcess(string s){//避免回文串的奇偶性讨论
string ans="";
if (s.size()==0) {
return "^$";
}
ans.append("^");
for (int i=0; i<s.size(); i++) {
ans.append("#");;
ans+=s[i];
}
ans.append("#$");
return ans;
}
string longestPalindrome(string s) {
string ans="";
if (s.size()==0||s.size()==1) {
return s;
}
string T=preProcess(s);
int n=T.length();
int *P=new int[n];//动态开辟数组;
int C=0,R=0;
int max=-1;
int start=0;//记录最长回文串的开始位置。
for (int i=1; i<T.size()-1; i++) {//计算P[i]
int i_mirrior=2*C-i;//求得i关于C点对称的点的坐标
P[i]=R>i?min(P[i_mirrior], R-i):0;//如果能被C为中心的回文串覆盖到的话,那么可以直接等于P[i_mirror]
while (T[i-P[i]-1]==T[i+P[i]+1]) {//尝试向左右两边扩展i为中心的回文串
P[i]++;
}
if (i+P[i]>R) {//如果已经超过右边界,则更新C,R。
C=i;
R=i+P[i];
}
if (P[i]>max) {
max=P[i];
start=i-P[i];
}
}
ans=s.substr((start-1)/2,max);
return ans;
}
};
参考http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html学习了一下Manacher算法来求解最长回文子串的问题。
manacher算法在O(n)时间内求解出最长回文子串。首先,我们要对字符串S进行预处理,目的是得到只存在奇数长度的回文子串的字符串,避免了回文串长度奇偶性的讨论。转化步骤是在S的每一个字符之间插入一个‘#’。
例如:S="abababa",T="#a#b#a#b#a#b#a#";
为了找到最长回文子串,我们以每个T[i]为中心,向两边扩展,使得T[i-d]...T[i+d]组成一个回文串。那么在T中的最长回文串长度为2d+1。仔细观察可以发现,在原字符串中的回文串长度正好是d;
例如:S="aba",T="#a#b#a#".在T中的最长回文串为"#a#b#a#",center 为b,i=3,d=3;T[3-3]...T[3+3]组成最长回文串,长度为7.正好对应原字符串中的最长回文串的长度为d,即3.
那么,如何求得T中的最长回文子串呢?
我们引入辅助数组P。P[i]表示以T[i]为中心的回文串向两边散开的长度,即上面说到的d。
例如:
(图片来自于http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html)由数组P,我们能立马得到最长回文串为abaaba。
现在我们想像一下在回文串abaaba中有一条直线画在中间。可以观察到直线两边的严格对称性。现在我们考虑一种更为复杂的情况。
假设我们已经计算得到了部分数组P的值,如图所示,我们已经有了P[1]-P[12],现在欲求解P[13].其中图中的黑色实线表示以a为中心的最长回文串,两边的L,R黑色点线表示以a为中心的最长回文串的两个边界。i'表示i以c为中心对称的点,即9。我们怎么能快速得到P[13]呢?
如图所示,两条绿色的横线表示了以i'与i为中心的最长回文子串的覆盖范围,显然P[i']=1.由于i‘为中心的回文子串,完全包含以C为中心的回文串中,且完全在C左边,由于对称性,显然,以i'为中心的回文串与以i为中心的回文串是完全对称的。因此,P[i]=P[i']=1;类似的,我们可以观察到P[12]=P[10]=0,P[13]=P[9]=1,P[14]=P[8]=0;
那么,现在我们考虑一种更为复杂的情况:
图中,绿色实线部分表示以i为中心以及以i'为中心的回文串中关于C严格对称的部分。穿越过center的部分,即绿色虚线的部分,也是严格对称的。但是,我们要注意到左边红线部分,以i'为中心的回文串已经超过了C为中心的回文串的左边界(图中红色部分)。我们由对称性能够得到的结论仅仅是p[15]>=5(绿色实线部分).要求解P[15],我们只能继续比较P[21]==P[9]吗??因为并不相等,所以我们得到结论P[15]=5.
所以总结一下:
--------------------------------------------------
if(P[i']<=R-i)
P[i]=P[i']
else
由右边界开始向外一对一对比较咯。
------------------------分割线------------------------
那么问题又来了。C与R的值是怎么维护的呢?假如刚才15为中心的回文串一路跨越了右边界R。那么我们就将C改为15,R改为以15为中心的右边界。为什么?将R右移,我们在求解接下来的P[i]的时候,是不是i为中心的子串被覆盖的可能性变大了呢?
总结一下:
如果i为中心的回文串扩展超过了边界R,则更新Center 为i,右边界为新的回文串的右边界。
------------------------分割线------------------------
最后,假如我们得到了T中的最长子串。如何对应到原字符串呢?在实际的代码中,参考上述网址的代码,在预处理的过程中,在前后边界还插入了特殊字符来避免边界讨论,,即S=“aba”,T=“^#a#b#a#$”,我们最后得到的最长回文串为T.substr(start_index,maxlen*2+1);对应于S的substr是什么呢?仔细观察可以得出,每个T中的index对应于S的下标为(index-1)/2。而长度我们前面分析过了,就是P数组中存的值,这里就是maxlen,因而。为s.substr((start_index-1)/2,maxlen); 希望能对大家有所帮助。