HDU 5371 Hotaru's problem manacher+(线段树or set)

题意,给定一个100000 的串,求他一个子串,使得将子串分成三部分有后,第一部分=第三部分,第一部分与第二部分对称(回文)

首先我们需要处理出以i为轴的回文串的两端,这个事情可以用Manacher算法完成,复杂度O(n)

http://blog.csdn.net/ggggiqnypgjg/article/details/6645824/

这个博客写的很好懂。不会的童鞋可以去学习一下这个算法,非常精妙。

好的现在我们已经会了这个算法,并获得了每个点为轴的串的右端点p[i]

很简单地可以处理出左端点。干脆把左右端点定义为left[i] 和right[i]

观察一下,以一部分和第二部分对称,第一部分=第三部分,会发现第二部分和第三部分也是对称的。

且由于两个部分对称,故回文串一定是偶数长度,也就是说是通过‘#’枚举出来的。(因为是数字代码里用-1代表#)

故对于现在枚举到的点,设为x,我们只枚举#点。

看图

听说语文不好的人都喜欢看图说话 = =

对于枚举的x。我们要找到一个y,使得y<=right[x],且x >= left[y]

看上面比较好理解,也可以自己画一画如果不是这样会怎么样。

这样的话,紫色的串和两边的绿色串分别对称。其中和左边的绿色关于x对称,右边的关于y对称。原因是没有超出他们最长对称区间。

因为每个区间是等长的,我们考虑最好计算的紫色区间,他的长度是y-x之类的,或者再加一,不要在意这些细节,这些后面处理。

要找最长的,我们需要使得y最大,这样区间最长。

于是问题就变成了,枚举x时(其实也就是枚举第二部分的起点时),找到一个最大的y并满足上面那个条件。

现在问题就演变成了如何找这个y,

无聊搜居然搜到了有的博主直接从right[i]开始往左找,这居然能过!(不过大概数据不好构造?),我真是= =。。。。。。。。。。。。。。。。。。。。。。。。。

撑死胆大的。

可以用set维护这个,但是作为非stl选手,我写了个线段树(妈妈我第一次在赛时写线段树T T 紧张,怀念前队友,好像跟之前的队友的时候我除了图论啥都不用写23333)

set我再学学吧,先来个线段树版本。

在线段树上,区间维护的是轴在这个区间内的回文串,最左端可以到的位置。

也就是对于(l, r, rt) 维护minleft[rt] = min(left[y] | l<=y<=r)

应该是用线段树辅助二分的思想?或者说用排序树的思想?总觉得他们是共通的。。> <

上面说了我们只用计算#这样的点的情况就可以了,所以可以重新对#编号,并寻找他们和原编号的对应关系(随便写几个就知道了)。然后现在left 和right的含义稍稍更新,不过也差不多。

现在我们从右向左枚举,对于枚举的x点,我们在1-left[x]区间内,优先向右儿子找,找到即返回。

找一个子树的要求是,minleft[rtofson]<= x,否则连这个区间里最小值都不小于x,就不可能找到left[y]<=x的y了。

每次写题解都这么啰嗦难怪我不喜欢写题解> <

其他看代码吧。

 1 #include <cstring>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 #define lson l,mid,rt<<1
 6 #define rson mid+1,r,rt<<1|1
 7 const int N = 200010;
 8 struct point{
 9     int left, y, right;
10     point(){};
11     point(int a, int b, int c){left = a, y = b, right = c;};
12 }info[N];// 之对于#号
13 int minleft[N<<1];
14 int p[N], left[N];
15 int ch[N], str[N];
16
17
18 void pushup(int x){
19     minleft[x] = min(minleft[x<<1] , minleft[x<<1|1]);
20 }
21
22 int query(int L, int R, int l, int r, int rt){
23     if(l == r){
24         if(info[l].left <= L)  return l;
25         return -1;
26     }
27     int mid = (l + r) >> 1, ans = -1;
28     //仅当minleft[rtson]的时候找那个儿子
29     //保证y最大,故优先往右,找到即返回
30     if(mid + 1 <= R && minleft[rt<<1|1] <= L) ans =query(L, R, rson);
31     if(ans != -1)return ans;
32     if(minleft[rt<<1] <= L) ans = query(L, R, lson);
33     return ans;
34 }
35 void modify(int pos, int l, int r, int rt){
36     if(l == r && l == pos){
37         minleft[rt] = info[l].left;
38         return;
39     }
40     int mid = (l + r )>> 1;
41     if(pos <= mid)modify(pos, lson);
42     else modify(pos, rson);
43     pushup(rt);
44 }
45 int main(){
46     int n, id, mxpos, i, j, ans, y;
47     int TC,tc;
48     scanf("%d", &TC);
49     for(tc = 1; tc <= TC; tc++){
50         scanf("%d", &n);
51         for(i = 1; i <= n; i++) scanf("%d", &ch[i]);
52         //manacher 处理
53         str[0] = -2;
54         id = 0;
55         for(i = 1; i <= n; i++){
56             str[i<<1] = ch[i];
57             str[i*2-1] = -1;
58         }
59         mxpos = 0;
60         ans = 0;
61         str[n<<1|1] =-1;
62         n = n << 1 | 1;
63         for(i = 1; i < n; i++){
64             if(mxpos > i){
65                 p[i] = min(p[2*id-i], mxpos - i);
66             }else{
67                 p[i] = 1;
68             }
69             for(; str[i+p[i]]==str[i-p[i]]; p[i]++){
70                 ;
71             }
72             ans = max(ans, p[i] - 1);
73             if(p[i] + i > mxpos){
74                 mxpos = p[i] + i;
75                 id = i;
76             }
77             left[i] = i - (p[i]-1);
78         }
79         id = 0;
80         for(i = 1; i < n; i++){
81             if(i & 1){
82                 //对所有的#重新编号,并处理左右端的#的标号
83                 info[++id] = point((left[i]+1)/2, i, (i + p[i]) /2);
84             }
85         }
86         ans = 0;
87         memset(minleft, 0x3f, sizeof(minleft));
88         for(i = id; i >= 1; i--){
89             y = query( i, info[i].right , 1, id, 1); //query()
90             if(y != -1)
91                 ans = max(ans, (y-i)*3);
92             modify(i, 1, id, 1);
93         }
94         printf("Case #%d: %d\n", tc, ans);
95     }
96     return 0;
97 }

PS:之前写Python作业,其实感觉先写出每个模块的作用对理清思路很有用。。。不过比赛的时候时间短+不太复杂的问题总是懒得写。。

HDU 5371 Hotaru's problem manacher+(线段树or set)

时间: 2024-10-27 10:11:50

HDU 5371 Hotaru's problem manacher+(线段树or set)的相关文章

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+尺取法)

题意: 给定一个有n个数字的序列,找出一个连续的子序列满足这样的条件: 1. 平均分成三段 2. 第一段与第三段一样 3. 第二段是第一段的倒序.求这样的子序列的最大长度. 数据范围:n~100000 解析: 我看网络上面很多的题解都是用O(n2/32)的做法水数据过去的,这种做法是先用mancher算法预处理出每个每个回文串最远所能抵达的位置,然后枚举每个位置i,再枚举其回文串的长度,然后枚举当前位置i到回文串所能抵达的最远距离,判断途径的位置j是,否有回文串能够到当前位置i.但是这种做法的复

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]是不是大于所穷举的长度,如果当前的满足三段,那么就跳出,继续