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

博客讲述了如何利用Manacher算法求解字符串的最长回文串,并结合线段树解决在一组回文串中找到满足特定条件的最长连续回文串问题。通过将问题转换为寻找数轴上圆心距离最大的一对圆,最终实现了O(nlogn)的时间复杂度解决方案。
摘要由CSDN通过智能技术生成

题意:

假设对于字符串A,它的反串为A'。

给出一个字符串,找出形式为AA'A的最长串的长度。

思路:

先用manacher跑一边,得到对于每一个位置i,以i为中心回文串的长度Mp[i]

以回文串AA'的中心i1为圆心,以AA'的半径Mp[i1]为半径,做圆O1

      以回文串A'A的中心i2为圆心,以A'A的半径Mp[i2]为半径,做圆O2

若要满足连续的AA'A

必有:

i1在圆O2上或在圆O2

     i2在圆O1上或在圆O1

那么问题转化为在一根最长为1e5的数轴上,有若干个圆,寻找满足条件的圆心距最大的一对圆。最长串的长度就等于3/2倍圆心距。

分析转化后的问题:

对于一个圆,如果他是满足条件的一对圆中左边的圆,那么这一对圆中右边的圆的最左端x必有x<=i。

即对于每一个圆心为i左边的圆,满足最大圆心距的右边的圆为,所有满足最左端x<=i的圆中,圆心最大的那个圆。

由此,我们可以使用线段树解决这个问题。

记录下所有圆的左端点,同时附上圆的圆心。按左端点从小到大排序。对于每个圆心为i的圆,查询左端点在[1,i]上左圆能覆盖最大的圆心。

共n个圆,每次查询的效率为logn。总的时间复杂度为O(nlogn)

代码:(904MS)

#include <bits/stdc++.h>
using namespace std;
#define ls rt<<1,l,mid
#define rs (rt<<1)|1,mid+1,r
#define mi (l+r)>>1
const int INF=0x3f3f3f3f;
const int MAXN=100100;

typedef struct Node{
    int x;int y;
    bool operator < (const Node &a)const{
        if(a.x==x)
            return a.y>y;
        return a.x>x;
    }
}Node;
int Ma[MAXN*2];
int Mp[MAXN*2];
int Mx[MAXN*2];
int len;
int v;
int st,en;
int prenum;
Node lp[MAXN*2];
Node pre[MAXN*2];
int maxn[MAXN*3];
int cut;
int ll;
int ans;
int pp;
Node t;
int k;
int T,n;
int buf[MAXN];
int query(int rt,int l,int r){
    if(maxn[rt]-k<=ans) return -INF;//剪枝,如果区间上最大圆心距不大于现有答案,则返回
    int mid=mi;
    if(st<=l&&r<=en){
        if(maxn[rt]<=v)//判断左圆是否能覆盖
            return maxn[rt];
        else{
            if(l==r)
                return -INF;
            else
                return max(query(rs),query(ls));
        }
    }
    int ansk=-INF;
    if(st<=mid)
        ansk=max(ans,query(ls));
    if(mid<en)
        ansk=max(ans,query(rs));
    return ansk;
}
void build(int rt,int l,int r){
    if(l==r){
        maxn[rt]=lp[pp].y;
        pp++;
        return ;
    }
    int mid=mi;
    build(ls);
    build(rs);
    maxn[rt]=max(maxn[rt<<1],maxn[(rt<<1)|1]);
    return ;
}
int manachar(){
    ll=0;ans=0;
    len=n;
    Ma[ll++]=-2;
    Ma[ll++]=-1;
    prenum=0;
    for(int i=0; i<len; i++){
        Ma[ll++]=buf[i];
        Ma[ll++]=-1;
    }
    Ma[ll]=-3;
    int mx=0,id=0;cut=0;
    for(int i=0; i<ll; 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;
        }
        Mx[i]=mx;
        if(Mp[i]>2){//当Mp[i]<=2时,说明回文串就是一个单字符,没有作为圆的必要。
            pre[prenum].x=Mp[i];
            pre[prenum].y=i;
            prenum++;
            lp[cut].x=i-Mp[i]+1;
            lp[cut].y=i;
            cut++;
        }
    }
    if(cut>1){
        memset(maxn,0,sizeof(maxn));
        sort(lp,lp+cut);
        sort(pre,pre+prenum);
        pp=0;
        build(1,1,cut);
        t.y=INF;
        for(int i=prenum-1;i>=0;i--){
            if(ans<pre[i].x){//剪枝,由于圆的半径排过序,圆的半径不大于现有答案时,转break
                st=1;
                t.x=pre[i].y;
                en=upper_bound(lp,lp+cut,t)-lp;
                v=pre[i].x+pre[i].y-1;
                k=pre[i].y;
                if(st<=en)
                    ans=max(ans,max(query(1,1,cut)-t.x,0));
               
            }else
                break;
        }
    }
    return ans;
}
int main(){

    scanf("%d",&T);
    for(int ppp=1;ppp<=T;ppp++){
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&buf[i]);
        printf("Case #%d: %d\n",ppp,manachar()/2*3);
        
    }
}
测试数据:
input:
4
10
2 3 4 4 3 2 2 3 4 4
17
1 1 1 1 5 4 4 5 1 1 1 1 5 4 4 5 1
5
1 2 3 4 5
15
2 3 4 4 3 2 2 3 4 4 3 2 2 3 4
output:
9
12
0
9

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 following condition: 
1. the first part is the same as the thrid part, 
2. the first part and the second part are symmetrical. 
for example, the sequence 2,3,4,4,3,2,2,3,4 is a N-sequence, which the first part 2,3,4 is the same as the thrid part 2,3,4, the first part 2,3,4 and the second part 4,3,2 are symmetrical. 

Give you n positive intergers, your task is to find the largest continuous sub-sequence, which is N-sequence. 
Input
There are multiple test cases. The first line of input contains an integer T(T<=20), indicating the number of test cases. 

For each test case: 

the first line of input contains a positive integer N(1<=N<=100000), the length of a given sequence 

the second line includes N non-negative integers ,each interger is no larger than  , descripting a sequence. 
Output
Each case contains only one line. Each line should start with “Case #i: ”,with i implying the case number, followed by a integer, the largest length of N-sequence. 

We guarantee that the sum of all answers is less than 800000. 
Sample Input
1
10
2 3 4 4 3 2 2 3 4 4
Sample Output
Case #1: 9









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值