浅谈(Manacher):https://www.cnblogs.com/AKMer/p/10431603.html
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2565
统计一下每个点做靠左的能覆盖它的回文串中心(left_i)和最靠右的能覆盖它的回文串中心(right_i)即可。
每次用#的(right_i-left_i)直接更新答案就行。
对于每个回文串中心,在(i-p_i+1)处的(right)与自己取一个(max),在(i+p_i-1)处与自己取(min)。然后倒着枚举更新(left)为后缀最小值,正着枚举更新(right)为前缀最大值即可。
注意更新答案的时候要判断当前点是否与自己的(left)和(right)都不相等,相等就不是双回文了。
时间复杂度:(O(n))
空间复杂度:(O(n))
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2e5+5;
int n,ans;
char s[maxn];
int p[maxn],left[maxn],right[maxn];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
int main() {
scanf("%s",s+1);
n=strlen(s+1);
for(int i=n;i;i--)
s[i<<1]=s[i],s[(i<<1)-1]='#';
s[0]='$',s[n<<1|1]='#',n=n<<1|1;
memset(left,63,sizeof(left));
int id=0,mx=0;
for(int i=1;i<=n;i++) {
p[i]=i<=mx?min(mx-i+1,p[(id<<1)-i]):1;
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(i+p[i]-1>mx)id=i,mx=i+p[i]-1;
int pos=i-p[i]+1;right[pos]=max(right[pos],i);
pos=i+p[i]-1;left[pos]=min(left[pos],i);
}
for(int i=1;i<=n;i++)right[i]=max(right[i-1],right[i]);
for(int i=n;i;i--)left[i]=min(left[i],left[i+1]);
for(int i=1;i<=n;i+=2)
if(right[i]!=i&&left[i]!=i)
ans=max(ans,right[i]-left[i]);
printf("%d
",ans);
return 0;
}