题意
对于两个串S1、S2,如果能够将S1的一个后缀移动到开头后变成S2,就称S1和S2循环相同。例如串ababba和串abbaab是循环相同的。
给出一个长度为n的串S,求满足下面条件的最大的L:
1. L<=n/2
2. S的L前缀和S的L后缀是循环相同的。
n<=1000000
n
<=
1000000
分析
网上的题解貌似都是推了些性质出来,但如果像我一样菜推不动性质的话还有一种比较易懂的做法。
不难发现就是要求一个最大的L满足存在一个数x,S[1,x]=S[n-x+1,n],且S[x+1,L]=S[n-L+1,n-x]。
假设只要满足S[1,x]=S[n-x+1,n]的话要怎么做呢?
我们可以把字符串进行变换,变成这个样子:S[1] S[n] S[2] S[n-1] S[3] S[n-2]…
设变换后的字符串为T,那么若有S[1,x]=S[n-x+1,n],则必有T[1,x*2]是一个回文串。
那么问题就转变成了求从T的起点开始的最长的双回文串。
直接manachar即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAX(x,y) x=max(x,y)
using namespace std;
const int N=1000005;
int n,s[N*2],len[N*2],f[N];
char str[N],a[N];
void manachar()
{
for (int i=1;i<=n*2+1;i+=2) s[i]=30;
for (int i=1;i<=n;i++) s[i*2]=a[i]-'a'+1;
int mx=0,pos=0;
for (int i=1;i<=n*2+1;i++)
{
if (mx>=i) len[i]=min(mx-i+1,len[pos*2-i]);
else len[i]=1;
while (i-len[i]>0&&i+len[i]<=n*2+1&&s[i-len[i]]==s[i+len[i]]) len[i]++;
if (i+len[i]-1>mx) mx=i+len[i]-1,pos=i;
if (s[i]==30) MAX(f[(i-len[i]+2)/2],len[i]/2*2);
}
}
int main()
{
scanf("%d%s",&n,str+1);
for (int i=1;i<=n/2;i++) a[i*2-1]=str[i];
for (int i=2,j=0;i<=n;i+=2,j++) a[i]=str[n-j];
n-=n&1;
manachar();
int ans=0;
for (int i=1;i<=n;i++)
{
MAX(f[i+1],f[i]-2);
if (i&1) continue;
if (len[i+1]/2*2==i) ans=max(ans,i+f[i+1]);
}
printf("%d",ans/2);
return 0;
}