求最长回文子串比较有名的一种算法,复杂度是O(n)的,(不要问我为什么是O(n))。
思路:尽量利用到之前遍历得到的回文信息。
表示以i为中心,最长的回文子串的半径有多长,并保存当前的回文子串往右延伸,最长能延伸到哪,即p[i]+i的最大值,存为right,其回文子串的中心点存为index。
考虑当下标为i时,要求p[i]。有几种情况:
1. 当i > right时,超出了之前得到的信息的范围,只能从i开始向两边遍历,求最长的回文范围了;
2. 当i < right时,说明现在还在之前遍历过的范围内,那么就把之前的信息利用起来。
p[i]最大可能为多大? 考虑一下以index为对称中心的i ‘,如果i ’ - p[i ‘] > left,也就是说i ‘为中心的最长回文子串完全被left-right包含,那么因为是对称的,所以i为中心的最长回文串也就是p[i ‘];如果i ’ - p[i ‘] < left,也就是说i ’ 为中心的最长回文子串超出了left-right的范围,那么以i为中心的回文串能超出right的范围吗。答案是不能,看一下图就知道如果超出了,两边又是对称的,那么left-right就延长了,而left-right已经是以i为中心的最长回文子串了,所以p[i] = min{p[i ‘], right - i}。
上面的方法,回文串必须是奇数长的,因为有中心点嘛,为了代码好些,普遍采用的放大是加特殊字符,将偶数长的也变成奇数长的。
代码如下:
class Solution {
public:
string pre(string s)
{
string res = "$";
for (int i = 0; i<s.size(); i++)
{
res += "#";
res += s[i];
}
res += "#";
res += "\0";
return res;
}
string longestPalindrome(string s) {
s = pre(s);
// cout << s << endl;
int p[100000] = { 0 };
int right = 0, index = 0;
int max = 0;
int max_i = 0;
for (int i = 1; i<s.size(); i++)
{
if (right > i)
p[i] = min(p[2 * index - i], right - i);
else
p[i] = 1;
while (s[i + p[i]] == s[i - p[i]])
p[i]++;
if (i + p[i] > right)
{
right = i + p[i];
index = i;
}
if (p[i]>max)
{
max = p[i];
max_i = i;
}
}
string res = "";
for (int i = max_i - p[max_i] + 1; i<max_i + p[max_i]; i++)
if (s[i] != '#')
res += s[i];
return res;
}
};
来源:CSDN
作者:lcxywfe
链接:https://blog.csdn.net/lcxywfe/article/details/72510254