hdu 5371 Hotaru's problem(manacher+尺取法)

题意:

给定一个有n个数字的序列,找出一个连续的子序列满足这样的条件:

1. 平均分成三段

2. 第一段与第三段一样

3. 第二段是第一段的倒序。求这样的子序列的最大长度。

数据范围:n~100000

解析:

我看网络上面很多的题解都是用O(n2/32)的做法水数据过去的,这种做法是先用mancher算法预处理出每个每个回文串最远所能抵达的位置,然后枚举每个位置i,再枚举其回文串的长度,然后枚举当前位置i到回文串所能抵达的最远距离,判断途径的位置j是,否有回文串能够到当前位置i。但是这种做法的复杂度太高了,不是正解。

还有一种更好的方法:

根据官方题解:

上面的条件抽象出来其实就是双回文串,所以题目就是求一个序列的最长双回文串。

具体做法是:

  1. 先用manacher算法O(n)求出每个元素的最大回文半径;
  2. 把每个元素看成一个圆心,那么两个点能构成双回文串必须满足的条件是他们在对方的圆内或圆上(画个示意图就理解了),所以接下来怎么利用最大回文半径呢。

具体做法:

  1. 可以将每个位置的左区间,以及每个位置的又区间分离。
  2. 将左区间放入A数组中,将右区间放入B数组中。
  3. 将A,B两个数组按照左边界排序。
  4. 那么A的右边界,就能看作是圆心;B的左边界,就能看作是圆心。

    那么符合条件的区间就是

    A.L<=B.L,且 B.L<=A.R<=B.R

  5. 由于左边界经过排序满足单调性,所以可以用尺取法来枚举两个区间,并维护最大值。

    manacher的复杂度是O(2n),排序的复杂度是O(nlogn),尺取法的复杂度是O(n),总复杂度是O(3n+nlogn)

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 100005;
int str[N*2], s[N], len;
int p[N*2], n;

void manacher() {
    len = n;
    for(int i = 0; i < len; i++) {
        str[i*2] = INF;
        str[i*2+1] = s[i];
    }
    str[len*2] = INF;
    len = len*2 + 1;
    int maxp = 0, id = 0;
    for(int i = 0; i < len; i++) {
        if(maxp > i) p[i] = min(maxp-i, p[id*2-i]);
        else p[i] = 1;
        while(str[i-p[i]] == str[i+p[i]] && i+p[i] < len && i-p[i] >= 0)
            p[i]++;
        if(i+p[i] > maxp) {
            maxp = p[i]+i;
            id = i;
        }
    }
}

struct Line {
    int L, R;
    Line() {}
    Line(int L, int R) : L(L), R(R) {}
    int length() { return R - L + 1;}
} A[N], B[N];
int an, bn;

bool cmp(Line a, Line b) {
    return a.L < b.L;
}

void prepare() {
    an = bn = 0;
    for(int i = 0; i < n; i++) {
        int len = (p[i*2] - 1) >> 1;
        if(len == 0) continue;
        A[an++] = Line(i - len, i - 1);
        if(i + 1 < n) B[bn++] = Line(i, i + len - 1);
    }
    sort(A, A+an, cmp);
    sort(B, B+bn, cmp);
}

int cal() {
    int maxv = 0, l = 0;
    for(int i = 0; i < bn; i++) {
        while(l < an && A[l].L <= B[i].L) {
            if(B[i].L <= A[l].R && A[l].R <= B[i].R) {
                maxv = max(maxv, A[l].R - B[i].L + 1);
            }
            l++;
        }
    }
    return maxv * 3;
}

int main() {
    int T, cas = 1;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%d", &s[i]);
        }
        manacher();
        prepare();
        printf("Case #%d: %d\n", cas++, cal());
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

hdu 5371 Hotaru's problem(manacher+尺取法)

时间: 2024-10-27 07:25:11

hdu 5371 Hotaru's problem(manacher+尺取法)的相关文章

HDU 5371 Hotaru&#39;s problem manacher+(线段树or set)

题意,给定一个100000 的串,求他一个子串,使得将子串分成三部分有后,第一部分=第三部分,第一部分与第二部分对称(回文) 首先我们需要处理出以i为轴的回文串的两端,这个事情可以用Manacher算法完成,复杂度O(n) http://blog.csdn.net/ggggiqnypgjg/article/details/6645824/ 这个博客写的很好懂.不会的童鞋可以去学习一下这个算法,非常精妙. 好的现在我们已经会了这个算法,并获得了每个点为轴的串的右端点p[i] 很简单地可以处理出左端

HDU 5371 Hotaru&#39;s problem(manacher + 枚举啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5371 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 foll

HDU 5371 Hotaru&#39;s problem (Manacher,回文串)

题意:给一个序列,找出1个连续子序列,将其平分成前,中,后等长的3段子序列,要求[前]和[中]是回文,[中]和[后]是回文.求3段最长为多少?由于平分的关系,所以答案应该是3的倍数. 思路:先Manacher求最长子串,利用期间所记录的P 数组,穷举一下所有可能的前两串,再用O(1)时间判断第3串是否符合要求. 具体做法: (1)P[i]记录的是以i为中心,从i-P[i]+1到i+P[i]-1这段都是回文.由于前两段之和必为偶数,所以必须选取str[i]为'#'的. (2)扫一遍每个'#',以其

hdu 5371 Hotaru&#39;s problem【manacher】

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5371 题意: 给出一个长度为n的串,要求找出一条最长连续子串.这个子串要满足:1:可以平均分成三段,2:第一段和第三段相等,3:第一段和第二段回文.求最大子串的长度. 代码: #include<stdio.h> #include<iostream> #include<math.h> #include<stdlib.h> #include<ctype.h&

HDU 5371——Hotaru&#39;s problem——————【manacher处理回文】

Hotaru's problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1765    Accepted Submission(s): 635 Problem Description Hotaru Ichijou recently is addicated to math problems. Now she is playing

HDU 5371 Hotaru&#39;s problem

manacher算法介绍 先用求回文串的Manacher算法,求出以第i个点和第i+1个点为中心的回文串长度,记录到数组c中 比如 10 9 8 8 9 10 10 9 8 我们通过运行Manacher求出第i个点和第i+1个点为中心的回文串长度 0 0 6 0 0 6 0 0 0 两个8为中心,10 9 8 8 9 10是个回文串,长度是6. 两个10为中心,8 9 10 10 9 8是个回文串,长度是6. 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,比如上边的两个字符

HDOJ 5371 Hotaru&#39;s problem manacher+优先队列+二分

先用求回文串的Manacher算法,求出以第i个点和第i+1个点为中心的回文串长度,记录到数组c中 比如 10 9 8 8 9 10 10 9 8 我们通过运行Manacher求出第i个点和第i+1个点为中心的回文串长度 0 0 6 0 0 6 0 0 0 两个8为中心,10 9 8 8 9 10是个回文串,长度是6. 两个10为中心,8 9 10 10 9 8是个回文串,长度是6. 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,比如上边的两个字符串,共享 8 9 10这一

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371(2015多校7)-Hotaru&#39;s problem(Manacher算法求回文串)

题目地址:HDU 5371 题意:给你一个具有n个元素的整数序列,问你是否存在这样一个子序列,该子序列分为三部分,第一部分与第三部分相同,第一部分与第二部分对称,如果存在求最长的符合这种条件的序列. 思路:用Manacher算法来处理回文串的长度,记录下以每一个-1(Manacher算法的插入)为中心的最大回文串的长度.然后从最大的开始穷举,只要p[i]-1即能得出以数字为中心的最大回文串的长度,然后找到右边对应的'-1',判断p[i]是不是大于所穷举的长度,如果当前的满足三段,那么就跳出,继续