zoukankan      html  css  js  c++  java
  • 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
    ", tc, ans);
    95     }
    96     return 0;
    97 }
    View Code

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

  • 相关阅读:
    问世即屠榜的bert
    写给日后面试的小朋友们~
    SQL笔记续补
    《姜子牙》视频笔记
    知识图谱之小米的落地与应用探索
    Pyspark ml
    一个小时学会用 Go 编写命令行工具
    C#设计模式-组合模式(Composite Pattern)
    C#设计模式-桥接模式(Bridge Pattern)
    C#设计模式-装饰器模式(Decorator Pattern)
  • 原文地址:https://www.cnblogs.com/bbbbbq/p/4722805.html
Copyright © 2011-2022 走看看