版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题意 :给你一个字符串,然后让你找一个子串,这个子串需要作为前缀、后缀 和 在中间出现。输出这个子串的最大长度。
思路:依旧是Next 数组的性质。首先明白Next数组的含义,Next[i] 表示 以 i 结尾的非前缀子串与前缀匹配的最大长度。
所以,整个字符串的后缀入手,如果Next[m] = 0,则肯定不存在这种子串,输出0即可。否则 Next[m] 肯定等于等于一个数,假设Next[m] = k ,表示该字符串前缀 与 后缀能匹配的长度,然后在 从 前面的位置中查看是否有Next[i] = k,如果有则k就是这个 最大长度。
不过需要特判一种情况,那就是形如 aaaaa这种字符串,前中后平分这个字符串,直接输出 m / 3即可。如果没出现在样例中,我真的可能想不起这种情况,后怕。。
!!!上述思路 有两处不够完善的地方
一个是Next[m]只是表示这个字符串前缀和后缀能匹配的最大长度,所以如果中间不存在最大长度,按照上述思路就输出0,可是最大长度不存在,那么次大长度呢?次次大长度呢?并没有判断,所以还得从最大到最小枚举检验。
二是我没有考虑这个题目这三个子串有没有交叉,显然在 本题中,是不可以 有重叠的,所以检验的时候区间范围应在去除前缀长度和后缀长度的中间检验。
修改后的 AC Code:
#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<cstdio>
#include<iomanip>
#include<sstream>
#include<algorithm>
using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define sRead(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define gc(x) scanf(" %c",&x);
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod 998244353
#define pdd pair<double,double>
const int N = 1000;
const int M= 1e6+5;
char s[M+5];
char c[M+5];
int Next[M+5];
int f[M+5];
void kmp_pre(int m){
int i = 0,j = Next[0] = -1;
while(i < m){
while(j!=-1&&s[i] != s[j]) j = Next[j];
Next[++i] = ++j;
}
}
bool ok(int k,int m){
for(int i = 2*k;i <= m - k;++i){//区间在中间
if(Next[i] == k) return 1;
}
return 0;
}
int main()
{
int T;
read(T);
while(T--){
mmt(Next,0);
scanf("%s",s);
int m = strlen(s);
bool op = 1;
for(int i = 1;i < m;++i){
if(s[i] != s[1]) {
op = 0;
break;
}
}
if(op == 1) {
cout<<m / 3<<endl;
continue;
}
kmp_pre(m);
int k = Next[m];
if(k == 0) puts("0");
else {
bool r = 1;
while(k != -1){//枚举前缀和后缀匹配的所有子串
if(ok(k,m)) {r = 0;break;}
else k = Next[k];
}
if(r == 1) puts("0");
else cout<<k<<endl;
}
}
}