题目大意: 给定一个数字串,找出其中最长的一个符合要求的数字串,要求数字串中第一部分和第三部分相同,第一部分和第二部分相反。
思路:首先看到第一部分和第二部分相反,可以联系到回文子串,本题是基于manacher算法上分析的,如果你还不懂manacher算法可以先去看下我之前的另一篇文章,有点长,只不过很详细。
好了,现在我们已经知道,第一部分加上第二部分是一个回文子串,然后题目又说第三部分等于第一部分,既然相等,我们可以知道第三部分和第二部分也是回文子串,我们姑且先把第一部分和第二部分合称“现回文子串”(为了解释方便),回文子串是中心对称,按理说应该可以是奇数,也可以是偶数,但是这道题具有特殊性,注意读题,题目说第一部分和第二部分和第三部分是连续的子串,就这说明在第一部分和第二部分中没有任何其他符号,偶+偶 == 偶,奇+奇 == 偶,所以“现回文子串”一定是偶数,
因为上文已经判断了“现回文子串”一定是偶数,所以,可以得知第一部分的长度和第二部分的长度是相等的,因为第三部分和第一部分相等,所以可以得出整个子串,是由三组长度相同的连续子串构成的。
因为通过manacher算法,我们可以得出p[]数组,也就是得到以该点为中心的最长回文子串,现在我们得到了这些回文子串,就相当于得到了许多的“现回文子串”(也就是第一部分+第二部分)。现在目的就很明确了,就是要找到第三部分。
因为第三部分和第一、第二部分大小相同,所以假设目前的点为 i 我们只要找到 i +p[i],如果它是的话,它的p[ ]应该大于或者等于p[i],看图看图。
所以说,只要p[i +p[i]] >= p[i],就成立了,就不用进入下图的while循环。大于的部分不用管,达到我们的要求就可以了。
如果没达到呢,我们 可以把p[ i ]里面的都试一下(如果p[i]范围太小就直接退出就可以了,看看代码的if和循环判断的条件),代码实现就是下面这一步了。
int tlen = p[i];
while(tlen > sum && p[i + tlen] < tlen)
tlen--;
细节:现在就到了运用刚刚花了点时间,讲“现回文子串”一定是偶数串的原因了,因为一定是偶数所以我们开始遍历,并且只i += 2,因为这样遍历到的,对应修饰后的数组都是0(用来修饰无意义的,如果不知道,请看上文发的manacher的讲解)不这样节省时间就会超时。
for(int i = 3;i < len;i+= 2){//从分析可知,该子串一定是偶数,所以从第二个偶数开始,并且只看偶数就可以了。
//补充一下i++的话可能会超时哦
if(p[i] > sum){//如果当前点的最大子串不如之前,直接跳过即可
int tlen = p[i];
while(tlen > sum && p[i + tlen] < tlen)
tlen--;
sum = max(sum,tlen);
}
}
代码实现:
#include<stdio.h>
#include<algorithm>
using namespace std;
int arr[100005];
int t[200010];
int p[200010];
int main(){
int T;
scanf("%d",&T);
for(int iu = 1; iu <= T;iu++){
int num;
scanf("%d",&num);
for(int i = 0; i < num;i++)
scanf("%d",&arr[i]);
//处理字符串
t[0] = -1;//独一无二的头
t[1] = 0;
int len = 2;
for(int i = 0; i < num;i++){
t[len++] = arr[i];
t[len++] = 0;
}
t[len] = -2;//独一无二的尾
//求出p[i]
int center = 0;
int max_right = 0;
for(int i = 1; i < len;i++){
int j = center * 2 - i;
if(i + p[j] <= max_right)
p[i] = p[j];
else
p[i] = 0;
while(t[i - p[i] - 1] == t[i + p[i] + 1])
p[i]++;
if(i + p[i] > max_right){
center = i;
max_right = i + p[i];
}
}
//本题的关键
int sum = 0;
for(int i = 3;i < len;i+= 2){//从分析可知,该子串一定是偶数,所以从第二个偶数开始,并且只看偶数就可以了。
//补充一下i++的话可能会超时哦
if(p[i] > sum){//如果当前点的最大子串不如之前,直接跳过即可
int tlen = p[i];
while(tlen > sum && p[i + tlen] < tlen)
tlen--;
sum = max(sum,tlen);
}
}
for(int i = 0; i <= len;i++)
printf("%d " ,p[i]);
printf("Case #%d: %d\n",iu,sum / 2 * 3);
}
}