作用:在O(n)时间内找到最长回文串
1.改造字符串:
字符串有奇偶之分,但偶回文串不好找对称中心,所以用此方法将字符串变为奇字符串。
方法:在字符之间和两边插入 ‘#’。
2.回文半径:
定义:以i为中心,最长回文串长度的一半
例如:
此时以
i
i
i为中心的回文串长度即为
p
[
i
]
−
1
p[i]-1
p[i]−1
3.加速盒子:
盒子范围:
[
l
,
r
]
[l,r]
[l,r]
维护右端点最靠右的最长回文串,利用数组p[i]对称点的值转移,从而节省时间。
例如:
在枚举完前
i
−
1
i-1
i−1个字符后,维护盒子
[
l
,
r
]
[l,r]
[l,r]
流程如下:
盒内:
若
i
≤
r
i\le r
i≤r,
i
i
i的对称点为
r
−
i
+
l
r-i+l
r−i+l,
若对称点的回文半径均在盒内,即
p
[
r
−
i
+
l
]
<
r
−
i
+
1
,
p
[
i
]
=
p
[
i
−
l
+
1
]
p[r-i+l]<r-i+1,p[i]=p[i-l+1]
p[r−i+l]<r−i+1,p[i]=p[i−l+1]
否则,此时在盒内的部分可以先赋值为
d
[
i
]
=
r
−
i
+
1
d[i]=r-i+1
d[i]=r−i+1,然后从
r
+
1
r+1
r+1开始暴力查看在盒外的部分有无以
i
i
i为中心点的对称字符
盒外:
否则,从
i
i
i开始暴力
如果
i
+
p
[
i
]
−
1
>
r
i+p[i]-1>r
i+p[i]−1>r,更新盒子
l
=
i
−
p
[
i
]
+
1
,
r
=
i
+
p
[
i
]
−
1
l=i-p[i]+1,r=i+p[i]-1
l=i−p[i]+1,r=i+p[i]−1
题目:
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
//const ull mod=2e64;
const int N = 2e7+2e6+20;
//const int p=131;
int p[N];//存字符的回文半径
string ss;//s:原字符串;ss:改变后的字符串
void add(string s){
ss+='$';
for(int i=0;i<s.size();i++){
ss+='#';
ss+=s[i];
}
ss+='#';
}
void manacher(){
p[1]=1;
for(int i=2,l,r=1;i<ss.size();i++){
if(i<=r)p[i]=min(p[r-i+l],r-i+1);//盒内两种情况
while(ss[i-p[i]]==ss[i+p[i]])p[i]++;//暴力,中心拓展
if(i+p[i]-1>r)l=i-p[i]+1,r=i+p[i]-1;//更新盒子左右边界
}
}
int main(){
string s;
cin>>s;
add(s);
manacher();
int ans=0;
for(int i=1;i<ss.size();i++){
ans=max(ans,p[i]);
}
cout<<ans-1;
return 0;
}