HDU 5371 Hotaru's problem(Manacher算法 回文串)

Hotaru's problem

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
Hotaru Ichijou recently is addicated to math problems. Now she is playing with N-sequence.
Let's define N-sequence, which is composed with three parts and satisfied with the following condition:
1. the first part is the same as the thrid part,
2. the first part and the second part are symmetrical.
for example, the sequence 2,3,4,4,3,2,2,3,4 is a N-sequence, which the first part 2,3,4 is the same as the thrid part 2,3,4, the first part 2,3,4 and the second part 4,3,2 are symmetrical.

Give you n positive intergers, your task is to find the largest continuous sub-sequence, which is N-sequence.
 

Input
There are multiple test cases. The first line of input contains an integer T(T<=20), indicating the number of test cases. 

For each test case:

the first line of input contains a positive integer N(1<=N<=100000), the length of a given sequence

the second line includes N non-negative integers ,each interger is no larger than  109  , descripting a sequence.
 

Output
Each case contains only one line. Each line should start with “Case #i: ”,with i implying the case number, followed by a integer, the largest length of N-sequence.

We guarantee that the sum of all answers is less than 800000.
 

Sample Input
   
   
   
   
1 10 2 3 4 4 3 2 2 3 4 4
 

Sample Output
   
   
   
   
Case #1: 9
 

Source
2015 Multi-University Training Contest 7
 
/*********************************************************************/

题意:给你一个具有n个元素的整数序列,问你是否存在这样一个子序列,该子序列分为三部分,第一部分与第三部分相同,第二部分与第一部分对称(例如2 3 4 4 3 2 2 3 4 显而易见,蓝色部分与绿色部分是相同的,而蓝色部分与粉色部分是对称的),要求给出形如所述的最长连续子序列。

放入出题人的解题报告

HDU 5371 Hotaru's problem(Manacher算法 回文串)_第1张图片

解题思路:首先,我们来回顾一下条件,因为第一部分与第三部分是相同的,而第一部分与第二部分又是对称的,所以我们可以得出第二部分与第三部分也是对称的的结论。

其次,我们来观察一下所给的例子,所谓的对称可以理解为是一个偶数长的回文串,于是,很容易就想到了用时间复杂度为O(n)的Manacher算法来处理回文串的长度问题,(一种计算最长回文子串的时间复杂度为O(n)的算法,想要学习一下的点链接吧Manacher算法),于是就拉了一下模板。

我们只需要判断两个相邻的回文串是否共享之间的一部分,就可以判断该子序列是否是题意规定的子序列。

于是傻乎乎地简单判了一下p[i]-1是否小于等于p[i+p[i]-1]-1,然后样例过了就以为对了,于是收获到了WA一枚

且看一组例子,就会明白仅仅如此是不正确的

10 

1 2 4 4 5 5 4 4 5 5

此例子对应的p数组-1后为

  0 0 2 0 6 0 6 0 2

此例子结果为6,这便与原来的想法有了矛盾。但我们只需要多添加一步暴力枚举p[i]--的情况,便又解决了一个难题。

具体做法:

  (1)p[i]记录的是以i为中心,从i-p[i]+1到i+p[i]-1这段都是回文。由于前两段之和必为偶数,所以必须选取s[i]为'-1'的。

  (2)扫一遍每个'-1',以其最长的回文开始穷举(仅需将p[i]--即可,然后找到右边对应的'-1',判断p[i]是不是大于所穷举的长度),直到3段都满足要求了,跳出此‘-1’,换下一个。

有什么问题都可以留下来,我们来讨论讨论增强理解。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<stdlib.h>
#include<cmath>
#include<string>
#include<algorithm>
#include<iostream>
#define exp 1e-10
using namespace std;
const int N = 100005;
const int inf = 1000000000;
int p[N*2],s[N*2],n;
void Manacher() {

    int id = 0, maxlen = 0;
    for (int i = n;i >= 0;--i) {
        s[i + i + 2] = s[i];
        s[i + i + 1] = -1;
   }
 s[0] = -2;
 for (int i = 2;i < 2 * n + 1;++i) {
        if (p[id] + id > i)p[i] = min(p[2 * id - i], p[id] + id - i);
        else p[i] = 1;
        while (s[i - p[i]] == s[i + p[i]])++p[i];
        if (id + p[id] < i + p[i])id = i;
        if (maxlen < p[i])maxlen = p[i];
    }
    //cout << maxlen - 1 << endl;
}
int main()
{
    int t,i,j,x,Max,k=1,c;
    scanf("%d",&t);
    while(t--)
    {
        Max=0;
        memset(p,0,sizeof(p));
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%d",&s[i]);
        Manacher();
        /*for(i=3;i<2 * n + 1;i+=2)
            printf(" %d",p[i]-1);*/
        for(i=3;i<2 * n + 1;i+=2)
            if(p[i]-1>Max)
            {
                c=p[i]-1;
                while(c>Max&&p[i+c]<c)
                    c--;
                Max=Max>c?Max:c;
            }
        printf("Case #%d: %d\n",k++,Max/2*3);
    }
    return 0;
}
菜鸟成长记

你可能感兴趣的:(算法,ACM,Manacher)