题意:给定一个字符串 s,求出最长的子串 k,使得 k 是 s 的前缀和后缀,并且在 s 中间也出现过一次。
分析:简单的KMP,通过 KMP 的 Next 数组从大到小枚举前后缀的长度,然后暴力判断一下中间有没有出现过就可以了。
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
const int N = 1E6+10;
char s[N];
int n,nxt[N];
void GetNext(char s[],int len,int nxt[])
{
nxt[0]=-1;
int j=0,k=-1;
while(j<len)
{
if(k==-1||s[j]==s[k])
{
++j;++k; nxt[j]=k;
}
else
{
k=nxt[k];
}
}
}
bool check(int len)
{
for(int i=len+len;i<=n-len;i++)
if(nxt[i]>=len) return true;
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s);
n=strlen(s);
GetNext(s,n,nxt);
int cnt=n;
while(nxt[cnt]*3>n) cnt=nxt[cnt]; //子串的长度至多为 n/3
while(true)
{
if(nxt[cnt]<=0)
{
puts("0");
break;
}
if(check(nxt[cnt]))
{
printf("%d\n",nxt[cnt]);
break;
}
cnt=nxt[cnt];
}
}
}