方法一:分治
回文有两种类型:奇数长度的回文和偶数长度的回文
字符串表示为s
,s
的最长回文子串用ans
表示。
奇数长度回文:
- 以
i
为对称中心,对称半径为j(j>=0)
(i-j) ... i ... (i+j)
总长度是2j-1
- 在
i-j
和i+j
在合理范围内时,如果有s[i-j]==s[i+j]
,而且ans
的长度小于2j-1
,则令ans=s.substr(i-j,2j-1)
,然后j++
,继续比较s[i-j]
和s[i+j]
,直到不相等,再重新选择对称中心,重新从上面的1开始执行。
偶数长度回文:
- 以
i
和i+1
为对称中心,半径为j(j>=1)
。 (i-j+1) ... i,i+1 ... (i+j)
总长度是2j
- 在
i-j+1
和i+j
在合理范围内时,如果有s[i-j+1]==s[i-j]
,而且ans
的长度小于2j
,则令ans=s.substr(i-j+1,2j)
.
实现:
string max_pal(string& s,int n)
{
string ans;
for(int i=0;i<n;i++)
{
//回文长度为奇数
for(int j=0;i-j>=0&&i+j<n&&s[i-j]==s[i+j];j++)
{
if(ans.length()<2*j-1)
ans=s.substr(i-j,2*j-1);
}
//回文长度为偶数
for(int j=1;i-j+1>=0&&i+j<n&&s[i-j+1]==s[i-j];j++)
{
if(ans.length()<2*j)
ans=s.substr(i-j+1,2*j);
}
}
return ans;
}
方法二:Manacher方法
统一考虑长度为奇数和偶数的回文串
- 在字符串首尾和每个字符中间都插入一个特殊字符,使得回文串都变成奇数长度的。
- 比如
abcdc
,插入特殊符号后变成#a#b#c#d#c
- 比如
- 定义一些符号
- 数组
p[i]
表示以i为中心的回文半径长度。 id
表示最大回文的mx
表示最大回文子串的右边界,即mx=id+p[i]
- 数组
- 在从
s[0]
扫描到s[n-1]
的过程中:
若mx>i
,有p[i]=min(p[2*id-1],mx-i)
。其中2*id-1
是i
关于id
对称的下标j=2*id-1
.
- 当
p[i]<mx-i
时,以s[j]
为中心的回文串全部包含在以s[id]
为中心的回文串中,而且i
,j
对称,以s[i]
为中心的回文串必定也包含在s[id]
为中心的回文串中.有p[i]=p[j]
.
- 当
p[i]>=mx-i
时,s[i]
的回文半径至少为mx-i
,向右扩展的部分要比较后才知道是不是构成回文串。
- 当
i>=mx
是,只能先让p[i]=1
,再进行后续匹配。
- 当
实现:
string Manacher(string &s)
{
//字符串处理
if(s.length()<=1)
return s;
int n=s.length();
string str="#";
for(int i=0;i<n;i++)
{
str+=s[i];
str+='#';
}
n=str.length();
vector<int>p(n);
int mx=id=0;
int best_mx=bext_id=0;
//从0开始遍历字符串
for(int i=0;i<n;i++)
{
if(mx>i)
p[i]=min(p[2*id-i],mx-i);
else
p[i]=1;
while(i-p[i]>=0&&i+p[i]<n&&str[i-p[i]]==str[i+p[i])
p[i]++;
if(p[i]+i>k)
{
id=i;
mx=p[i]+i;
if(mx-id>best_mx-best_id)
{
best_mx=mx;
best_id=id;
}
}
}
string ans;
for(int i=2*best_id-best_mx+1;i<bext_mx;i++)
{
if(str[i]!='#')
ans+=str[i];
}
return ans;
}
时间复杂度为O(n)