hdu5371 Hotaru's problem (Manacher)

/*像我这种菜鸡果然还是要写写博客,不然有的题目真是过去就过去了的状态。自己在硬盘里的代码也不太好管理,等以后写个软件来搞就好了 -  - */

题意:

给出一个串,找出满足以下条件的最长子串:

1. the first part is the same as the thrid part,

第一个部分和第三个部分一样。
2. the first part and the second part are symmetrical.

第一个部分和第二个部分对称。

思路:

这道题其实就是马拉车算法的简单应用

从题意出发  我们发现其实他想说的是 :

(设把s的一个子串分成三个子串   第i个子串为si)

|s1|==|s2|==|s3|

s1+s2是回文串

s2+s3是回文串

所以我们用马拉车(就爱说这个 -   -)可以知道其实就是求最长的一个子串满足:

设:s1和s2中间的分隔符为中心的回文串的长度p1

     s2和s3中间的分隔符为中心的回文串的长度p2

min(p1,p2)-1>=|s2|

一开始是对于每个分隔符暴力找  若找到则记录长度 

但是TLE了 -  -

所以  对j的循环做了一些优化 若小于所记录的就跳出

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std;

const int MAXN = 1e6+10;

int p[MAXN];

//适用于正负整数
template <class T>
inline bool scan_d(T &ret) {
   char c; int sgn;
   if(c=getchar(),c==EOF) return 0; //EOF
   while(c!='-'&&(c<'0'||c>'9')) c=getchar();
   sgn=(c=='-')?-1:1;
   ret=(c=='-')?0:(c-'0');
   while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
   ret*=sgn;
   return 1;
}

int Proc(int pszIn[],int pszOut[],int len)
{
    int nLen=1;
    pszOut[0]=0;
    int i=0;
    while(i<len)
    {
        pszOut[nLen++]=-1;
        pszOut[nLen++]=pszIn[i];
        i++;
    }
    pszOut[nLen++]=-1;
    pszOut[nLen]=-2;
    return nLen;
}

void Manacher(int *p,int *str,int len)
{
    int mx=0,id=0;
    for(int i=0;i<len;i++)
    {
        p[i]=mx>i?min(p[2*id-i],mx-i):1;
        while(str[i+p[i]]==str[i-p[i]])p[i]++;
        if(i+p[i]>mx)
        {
            mx=i+p[i];
            id=i;
        }
    }
}

int check(int len)
{
    long long cnt=0;
    int ret=0;
    for(int i=1;i<=len;i+=2)
    {
        if(p[i]>2)
            for(int j=(i+min(((p[i]>>1)<<1),((len>>1)/3)*2));j>i&&ret<(j-i)+1;j-=2,cnt++)
                if(min(p[i],p[j])>=(j-i)+1)
                    ret=(j-i)+1;
    }
    return ret;
}

int in[MAXN],out[MAXN<<2];
int main()
{
    int T,cas=1;
    scan_d(T);
    while(T--)
    {
        int n;
        scan_d(n);
        for(int i=0;i<n;i++)
            scan_d(in[i]);
        n=Proc(in,out,n);
        Manacher(p,out,n);
        printf("Case #%d: %d\n",cas++,(check(n)/2)*3);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值