【HDU5371】Hotaru's problem(Manacher + set)

利用Manacher求出每两个数字中间位置的回文长度

之后利用set进行维护,大题思路如下:

要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,比如上边的两个字符串,共享 8 9 10这一部分。 也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样。 因为我们已经记录下来以第i个点和第i+1个点为中心的回文串长度, 那么问题可以转化成,相距x的两个数a[i],a[i+x],满足a[i]/2>=x 并且 a[i+x]/2>=x,要求x尽量大

这可以用一个set维护,一开始集合为空,依次取出a数组中最大的元素,将其下标放入set中,每取出一个元素,再该集合中二分查找比i+a[i]/2小,但最大的元素,更新答案。 然后查找集合中比i-a[i]/2大,但最小的元素,更新答案。

参考官方题解:http://bestcoder.hdu.edu.cn/blog/

代码如下:

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100005;
int Ma[maxn * 2],Mp[maxn * 2];
int arr[maxn],len,n;
set<int>st;
set<int>::iterator it;
struct Node{
    int id,val;
}node[maxn * 2];
int cmp(Node p,Node q){
    return p.val > q.val;
}
int read(){
    char z=getchar();
    while(z <'0'|| z>'9') z = getchar();
    int ans=0;
    while(z>='0' && z<='9') {
        ans=ans * 10 + z - '0';
        z = getchar();
    }
    return ans;
}
//Manacher
void Manacher(){
    int l = 0;
    Ma[l++] = -2;
    Ma[l++] = -1;
    for(int i = 1; i <= n; i++){
        Ma[l++] = arr[i];
        Ma[l++] = -1;
    }
    int mx = 0,id = 0;
    len = l;
    for(int i = 0; i < l; i++){
        Mp[i] = mx > i ? min(Mp[2 * id - i],mx - i) : 1;
        while(Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i] ++;
        if(i + Mp[i] > mx){
            mx = i + Mp[i];
            id = i;
        }
    }
}
//debug
void debug(){
    for(int i = 0; i < len; i++)
        printf("%2d ",i);
    puts("");
    for(int i = 0; i < len; i++)
        printf("%2d ",Ma[i]);
    puts("");
    for(int i = 0; i < len; i++)
        printf("%2d ",Mp[i]);
    puts("");
}
//main
int main(){
    int T,Case = 1;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        memset(Mp,0,sizeof(Mp));
        for(int i = 1; i <= n; i++)
            arr[i] = read();
        Manacher();
        //debug();
        st.clear();
        for(int i = 1; i <= n; i++){
            node[i].id = i;
            node[i].val  = (Mp[i * 2 + 1] - 1) / 2;
        }
        sort(node + 1,node + n + 1,cmp);
        int ans = 0;
        for(int i = 1; i <= n; i++){
            int _find1 = node[i].id - node[i].val;
            int _find2 = node[i].id + node[i].val;
            //printf("%d %d\n",node[i].id,node[i].val);
            st.insert(node[i].id);
            it = st.lower_bound(_find1);
            ans = max(ans,node[i].id - *it);
            it = st.upper_bound(_find2);
            it --;
            ans = max(ans,*it - node[i].id);
        }
        printf("Case #%d: %d\n",Case++,ans * 3);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值