前言
马拉车算法在O(n)的情况下求解一个字符串的最长回文子串的长度
思路
首先,我们问了解决问题方便,不用讨论奇偶,在首尾处和原字符串每个相邻两个字符中间插入一个分隔符。
设置一个辅助数组p[i],表示以字符s[i]为中心的最长回文字串的最右字符到s[i]的长度。比如以s[i]为中心的最长回文子串是s[l,r],那么p[i]=r-l+1.
关于p数组的求法:
首先从左到右依次计算len[i],当计算len[i]时,len[j] (0<j<i)已经被计算过了。设mx为之前计算中最长回文子串的右端点的最大值,并且设置取得这个最大值的位置为id.
分两种情况讨论
情况一:当i<mx时,若len[j]<mx-i,则说明以j为中心的回文串一定在以id为中心的回文串内部。以i为中心的回文串的长度与以j为中心的回文串长度相同。 若len[j]>=mx-i,由于对称性,我们知道以i为中心的回文串可能会延伸到p之外,而大于p的地方我们还没有匹配,所以我们要从p+1的位置开始一个一个的匹配,更新mx,id,len[i].
情况二:i>p,一个一个匹配,更新id,mx,len[i].
代码
const int maxn=1e5+10;
char str[maxn];
char p[maxn<<1];
int l[maxn<<1];
void malache()
{
int len=strlen(str);
p[0]='#';p[1]='#';
for(int i=0;i<len;i++)
{
p[(i+1)<<1]=str[i];
p[(i+1)<<1|1]='#';
}
int mx=0,id=0,ans=0;
for(int i=0;i<2*len+1;i++)
{
if(i<mx) l[i]=min(l[2*id-i],mx-i);
else l[i]=1;
while(p[i-l[i]]==p[i+l[i]]) l[i]++;
if(l[i]+i>mx) id=i,mx=i+l[i];
ans=max(ans,l[i]);
}
cout<<ans-1<<endl;
}