K - 弗吉桑
原题地址
1.大概题意:给定一个数组,求最长的回文子串长度。
2.思路分析: Manacher 算法裸题。
简单讲解一下 Manacher 算法:首先这是一种高效的找一个序列最长回文串的算法,时间复杂度为 O(n).其实该算法就是利用之前算好的回文串长度来优化当前回文串长度查找。其中有几个变量要提前预设一下:
p[ i ]:代表以 i 为中心的回文串长度。
mx : 当前回文串的最右端的位置。
id : 当前最右端回文串的中点位置。
① :对原字符串的预处理.就是在字符中间添加 # (这里以字符串为例)。这样就可以解决奇偶长度字符串的问题了。
② 遍历数组:对于任意 i 的位置,取得关于 id 对称的位置 j。
第一种情况:mx>i. 这时还有两种情况:①:mx-i>p[ j ]。此时以 j 为中心的回文子串包含在以 id 为中心的回文子串内,所以: p[ i ]=p[ j ]=p[ id*2-1 ]. ②:mx-i<=p[ j ].由前面的分析可知,此时向右延伸的最大范围就是 mx-i.剩下的只能对比判断,那么p[ i ]=mx-i;
第二种情况:mx<=i.那么只能 p[ i ]=1.
3.核心代码:
1)
Manacher 算法:正如前面所说:
P[ I ]=mx>I ? min(p[ id*2-1 ] , mx-I ):1。
那么当 s[ i+p[ I ] ]==s[ i-p[ I ] ]时,自然 p[ I ]++;最后就是更新 id 和 mx 的值。
2)
主程序:
因为我们的题目是数组,不是字符串,所以在每一个数组与元素中间插入 -1,开头为 -2.然后跑一遍 manacher 即可,不要忘记更新 ans 。最后输出即可。
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 1000005
typedef long long ll;
ll a[maxn]={0};
ll p[maxn]={0};
void manacher(ll *s,ll len)
{
p[0]=1;
ll mx=0,id=0;
for(ll i=1;i<len;i++){
p[i]=mx>i?min(p[id*2-i],mx-i):1;
while(s[i+p[i]]==s[i-p[i]])p[i]++;
if(i+p[i]>id+p[id]){
id=i;
mx=i+p[i];
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
ll i;
scanf("%d",&n);
memset(a,0,sizeof(a));
for(i=0;i<n;i++)scanf("%lld",&a[i]);
ll len=n;
for(i=len;i>=0;i--){
a[(i<<1)+1]=-1;
a[(i<<1)+2]=a[i];
}
a[0]=-2;
len=len*2+2;
manacher(a,len);
ll ans=0;
for(i=0;i<len;i++){
ans=max(ans,p[i]-1);
}
printf("%lld\n",ans);
}
}