http://www.lintcode.com/zh-cn/problem/longest-palindromic-substring/
问题描述:
给出一个字符串(假设长度最长为1000),求出它的最长回文子串,你可以假定只有一个满足条件的最长回文串。
样例
给出字符串 “abcdzdcab”,它的最长回文子串为 “cdzdc”。
最开始想到的解决办法是暴力法,但是会导致复杂度很大,约为
O(n3)
优化后为
O(n2)
。但是有种Manacher算法可以以线性复杂度解决最长回文子串问题:
首先,为了方便实现字符串,需要将所有的字符串预处理成奇数串。
例如字符串
ABACAB
预处理后成为(加$是为了方便从1开始计算):
$#A#B#A#C#A#B#
然后求左右扩张长度:
S:# A # B # A # C # A # B #
P:1 2 1 2 1 2 1 6 1 2 1 2 1
P为字符串每个元素的向左/右扩张的长度,最大的那个便是最长回文子串的长度+1。
算法求扩展长度:
我们假设我们已经处理了前i-1个值,需要求第i个值,设现在求出的最大回文串的中间字符为max_mid,右边缘(目前最大回文串的向右扩展长度)为max_r。i通过max_mid对称的字符是j(2×max_mid-i)。
若此时i小于目前最大回文子串的边界max_r,那么说明以i为中心的回文串至少有一部分包含在目前最大回文串里。
a = P[j](j的扩展长度)
b = max-i (j到max_l的长度)
根据图可以得出,以i为中心的回文串长度至少是min(a,b)
代码实现:
#include <iostream>
#include <string>
#include <cstring>
#include <climits>
using namespace std;
class Solution {
public:
/*
* @param s: input string
* @return: the longest palindromic substring
*/
typedef struct res_pos{
int pos;
int n;
}res_pos;
//预处理字符串
void init(const string &s, string &res)
{
int l = s.length();
res = "$";
for(int i = 0; i < l; ++i)
{
res = res + "#" + s[i];
}
res += "#";
}
//manacher算法,求p数组
void manacher(const string &s, int *p)
{
int len = s.length();
int max_mid = 0;
int max_r = 1;
p[0] = 1;
for(int i = 1; i < len; i++)
{
if(max_r > i)
p[i] = min(p[2*max_mid-i], max_r - i);
else
p[i] = 1;
for(; s[i + p[i]] == s[i - p[i]]; p[i]++);
if(max_r < i+p[i])
{
max_r = i + p[i];
max_mid = i;
}
}
}
//求出最长回文子串中点的位置和扩展长度
res_pos maxx(int *p, int n)
{
int max = -1;
res_pos res;
for(int i = 1; i < n; i++)
{
if(max < p[i]){
max = p[i];
res.pos = i;
}
}
res.n = max;
return res;
}
string longestPalindrome(string &s) {
// write your code here
string after_str;
int p[2100];
init(s, after_str);
manacher(after_str, p);
res_pos pos = maxx(p, after_str.length());
string result;
for(int i = pos.pos - pos.n + 1; i < pos.pos + pos.n; i++)
{
if(after_str[i] != '#')
result += after_str[i];
}
return result;
}
};