码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-08-12 11:38:48      阅读:119      评论:0      收藏:0      [点我收藏+]

标签:hdu   5371   

题意:

给定一个有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+尺取法)

标签:hdu   5371   

原文地址:http://blog.csdn.net/helloworld10086/article/details/47440727

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!