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 }
View Code

 

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

 

posted @ 2015-08-11 23:56  bbbbq  阅读(192)  评论(0编辑  收藏  举报