hdu3068 manacher算法 最长回文子…

解题报告

题目 http://acm.hdu.edu.cn/showproblem.php?pid=3068
题目大意 :求给定串的最长回文子串(2009多校题目)

算法 manacher;

思路 :枚举每个点向左向右扩展,看最远能扩展到哪儿.但是普通的枚举是n^2的,肯定超时。现在我们想kmp或扩展kmp一样,给字符串定义一个nex数组,nex[i]表示以i为中心最远能向右扩展的长度,使得s[i – nex[i] + 1……. i + nex[i]- 1]形成的回文。然后我们利用这个数组,在O(n)的时间内求出每个inex[i]。在其他算法中,奇数回文和偶数回文经常给我们带来麻烦,这个算法中,我们第一步要进行的是将每个字符后边(包括开头)加入一个字符(不在串儿的字符集中就行),一般用’#”. 这样就都转换为了奇数的情况。

例如       abba  (偶)

         改为   #a#b#b#a# (最长为以第3#为中心)

             aba    (奇) 

改为    #a#b#a#  (最长为以b为中心)      

剩下的就是在我们知道了nex[0]…….nex[i – 1] 如何求nex[i] p记录前i-1个字符中以某个字符id为中心最远能向右扩展到的位置。 

代码:

void Get_nex(){

    int i, p, id;

    p = 0;

    fup(i, 1, L2){

       if(p > i) nex[i]= MIN(nex[2 * id - i], p -i);

       else nex[i] = 1;

       for(; tem[i+ nex[i]] == tem[i - nex[i]]; nex[i]++);

       if(p <i + nex[i]) p = i + nex[i],id = i;

    }

}

 

 

if(p > i) nex[i]= MIN(nex[2 * id - i], p -i);

这是代码中最核心的一句话,它的作用是让我们在以i为中心向左右扩展时,尽量减少重复的比较,当p > i 时会有两种情况。设j = 2 *id – i  id为中心i的对称位置。

第一:当以i为中心的回文和以j为中心的回文都在以id为中心的回文中时。

 由于回文的对称性,这时nex[i]最小为nex[j] 这是表达式前半部分。

第二:当以i为中心的回文或以j为中心的回文不在以id为中心的回文中时。

        q a b a c a b a c           

hdu3068 <wbr>manacher算法 <wbr>最长回文子串 hdu3068 <wbr>manacher算法 <wbr>最长回文子串 hdu3068 <wbr>manacher算法 <wbr>最长回文子串           j   id   i

或:

c a b a c a b q c           

hdu3068 <wbr>manacher算法 <wbr>最长回文子串 hdu3068 <wbr>manacher算法 <wbr>最长回文子串  hdu3068 <wbr>manacher算法 <wbr>最长回文子串             id   i

这时以i为中心的最长回文不一定大于nex[i],但是由于回文的对称性,他最小是p-i(nex[j]大时)nex[j]

收获以经验 :又学到了一种新算法,happy

提交情况 accepted 1

AC code

 

#include <cstdio>

#include <cstring>

#include <cctype>

#include <cmath>

#include <cstdlib>

#include <ctime>

#include <map>

#include <set>

#include <vector>

#include <algorithm>

using namespace std;

 

typedef int I64;

typedef double real;

 

#define MAX(a, b) ((a) > (b) ?(a) : (b))

#define MIN(a, b) ((a) < (b) ?(a) : (b))

#define mem(a, b) memset(a, b, sizeof(a))

#define fup(i, a, b) for(i = a; i < b; ++i)

#define fdn(i, a, b) for(i = a; i > b; --i)

#define INF 210000000

 

I64 Gcd(I64 a, I64 b){return b ? Gcd(b, a% b) : a;}

I64 Lcm(I64 a, I64 b){return a / Gcd(a, b)* b; }

 

#define MAXL 110100

 

char s[MAXL], tem[MAXL * 2];

I64 L1, L2, nex[MAXL* 2];

 

void Get_tem(){

    int t = 0, i = 0, k = 1;

    tem[0] = '$';

    while(s[i]!= '\0'){

       tem[k ++] = t ?s[i ++] : '#';

       t ^= 1;

    }

    tem[k++] = '#';

    tem[L2 = k] = '\0';

}

 

void Get_nex(){

    int i, p, id;

    p = 0;

    fup(i, 1, L2){

       if(p >i) nex[i] = MIN(nex[2 * id - i],p - i);

       else nex[i] = 1;

       for(; tem[i+ nex[i]] == tem[i - nex[i]]; nex[i]++);

       if(p <i + nex[i]) p = i + nex[i],id = i;

    }

}

 

 

int main(){

    I64 i;

    while(~scanf("%s", &s)){

       Get_tem();

       Get_nex();

       I64 min = 0;

       fup(i, 1, L2)

           if(min < nex[i] - 1) min = nex[i]- 1;

       printf("%d\n",min);

    }

    return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值