Manacher算法

Manacher算法是解决求给定字符串中的最长回文串问题的,时间复杂度为O(N)。

首先给出一篇很好的文章:A simple linear time algorithm for finding the longest palindrome substring

不过上面这篇文章是英文的,对于英文不好的朋友,可以看这里:O(N)回文串算法

算法比较容易理解,下面就贴个模板吧,另外还有两个例题:HDU_3068 、 Ural_1297

Manacher模板:

/*
str : 原字符数组,在每两个相邻的字符之间加入了一个‘#’(第一个之前
      和最后一个之后也要加一个'#'),同时为了避免(1)处产生数组越
      界,在最开始加入一个'$',在最后加入一个0 ;
P   : 辅助数组,P[i]表示以i为中心,向右最多可以“走出”多少个字符,
      ( P[i] - 1 ) * 2 + 1 就是以i为中心的回文串的长度;
a   : a = max{ a+P[a] > j + P[j] | j < i } ;
mx  : mx = a + P[a]  ;  以a为中心的回文串为:str[ a-P[a]+1 ....... a+P[a]-1 ]

*/
void solve(){
    int mx , a , i , j , ans = 2 ;
    P[0] = 1 ;  a = 0 ; mx = 1 ;
    rep(i , 1 , len){
        if(i < mx){
            P[i] = MIN( P[2*a-i] , P[a]+a-i ) ;
        }
        else{
            P[i] = 1 ;
        }
        for( ; str[i+P[i]]==str[i-P[i]] ; ){    //(1)
            P[i]++ ;
        }
        if( i + P[i] > mx ){
            mx =i + P[i];
            a = i ;
        }
    }
    rep(i , 0 , len)    checkmax( ans , P[i] ) ;
    printf("%d\n",ans-1);
}

HDU_3068 :

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <list>
#include <ctime>
#include <sstream>

#define rep(i,initial,end)  for(i=(initial);i<(end);i++)
#define MAX(x,y)            ((x)<(y)?(y):(x))
#define MIN(x,y)            ((x)<(y)?(x):(y))
#define SZ(a)               (sizeof(a))
#define MEM(a)              ( memset((a) , 0 , sizeof(a)) )
#define MEME(a)             ( memset((a) , -1 , sizeof(a)) )
#define MEMX(a)             ( memset((a) , 0x3f , sizeof(a)) )

using namespace std;

typedef long long               LL ;
typedef unsigned long long      ULL ;
typedef unsigned int            uint ;
typedef unsigned char           uchar ;

template<class T>inline void checkmax(T &a , T b){ if(a<b) a = b; }
template<class T>inline void checkmin(T &a , T b){ if(a>b) a = b; }

const int inf = 1 << 30 ;
const double eps = 1e-7 ;
const LL oo = 100000000LL * 100000000LL ;   //1e16
const LL Mod = 1000000007 ;     //1e9 + 7

const int NN = 110000 + 10 ;
char ch[ NN ] , str[ NN << 1 ] ;
int len ;
int P[NN<<1] ;

/*
str : 原字符数组,在每两个相邻的字符之间加入了一个‘#’(第一个之前
      和最后一个之后也要加一个'#'),同时为了避免(1)处产生数组越
      界,在最开始加入一个'$',在最后加入一个0 ;
P   : 辅助数组,P[i]表示以i为中心,向右最多可以“走出”多少个字符,
      ( P[i] - 1 ) * 2 + 1 就是以i为中心的回文串的长度;
a   : a = max{ a+P[a] > j + P[j] | j < i } ;
mx  : mx = a + P[a]  ;  以a为中心的回文串为:str[ a-P[a]+1 ....... a+P[a]-1 ]

*/
void solve(){
    int mx , a , i , j , ans = 2 ;
    P[0] = 1 ;  a = 0 ; mx = 1 ;
    rep(i , 1 , len){
        if(i < mx){
            P[i] = MIN( P[2*a-i] , P[a]+a-i ) ;
        }
        else{
            P[i] = 1 ;
        }
        for( ; str[i+P[i]]==str[i-P[i]] ; ){    //(1)
            P[i]++ ;
        }
        if( i + P[i] > mx ){
            mx =i + P[i];
            a = i ;
        }
    }
    rep(i , 0 , len)    checkmax( ans , P[i] ) ;
    printf("%d\n",ans-1);
}

int main(){
    int i , j ;
    while( scanf("%s",ch) == 1 ){
        len = strlen( ch ) ;
        str[0] = '$' ; str[1] = '#' ;
        rep(i , 0 , len){
            str[i*2 + 2] = ch[i] ;
            str[i*2 + 3] = '#' ;
        }
        len = len << 1 ; len += 2 ; str[len] = 0 ;
        solve() ;
    }
    return 0 ;
}

Ural_1297:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <list>
#include <ctime>
#include <sstream>

#define rep(i,initial,end)  for(i=(initial);i<(end);i++)
#define MAX(x,y)            ((x)<(y)?(y):(x))
#define MIN(x,y)            ((x)<(y)?(x):(y))
#define SZ(a)               (sizeof(a))
#define MEM(a)              ( memset((a) , 0 , sizeof(a)) )
#define MEME(a)             ( memset((a) , -1 , sizeof(a)) )
#define MEMX(a)             ( memset((a) , 0x3f , sizeof(a)) )

using namespace std;

typedef long long               LL ;
typedef unsigned long long      ULL ;
typedef unsigned int            uint ;
typedef unsigned char           uchar ;

template<class T>inline void checkmax(T &a , T b){ if(a<b) a = b; }
template<class T>inline void checkmin(T &a , T b){ if(a>b) a = b; }

const int inf = 1 << 30 ;
const double eps = 1e-7 ;
const LL oo = 100000000LL * 100000000LL ;   //1e16
const LL Mod = 1000000007 ;     //1e9 + 7

const int NN = 110000 + 10 ;
char ch[ NN ] , str[ NN << 1 ] ;
int len ;
int P[NN<<1] ;

/*
str : 原字符数组,在每两个相邻的字符之间加入了一个‘#’(第一个之前
      和最后一个之后也要加一个'#'),同时为了避免(1)处产生数组越
      界,在最开始加入一个'$',在最后加入一个0 ;
P   : 辅助数组,P[i]表示以i为中心,向右最多可以“走出”多少个字符,
      ( P[i] - 1 ) * 2 + 1 就是以i为中心的回文串的长度;
a   : a = max{ a+P[a] > j + P[j] | j < i } ;
mx  : mx = a + P[a]  ;  以a为中心的回文串为:str[ a-P[a]+1 ....... a+P[a]-1 ]

*/
void solve(){
    int mx , a , i , j , ans = 2 ;
    P[0] = 1 ;  a = 0 ; mx = 1 ;
    rep(i , 1 , len){
        if(i < mx){
            P[i] = MIN( P[2*a-i] , P[a]+a-i ) ;
        }
        else{
            P[i] = 1 ;
        }
        for( ; str[i+P[i]]==str[i-P[i]] ; ){    //(1)
            P[i]++ ;
        }
        if( i + P[i] > mx ){
            mx =i + P[i];
            a = i ;
        }
    }
    rep(i , 0 , len)    checkmax( ans , P[i] ) ;
    rep(i , 0 , len){
        if( ans == P[i] ){
            rep(j , i-P[i]+1 , i+P[i] )
                if( str[j]!='$' && str[j]!='#')  printf("%c",str[j]);
            printf("\n");
            return ;
        }
    }
}

int main(){
    int i , j ;
    while( scanf("%s",ch) == 1 ){
        len = strlen( ch ) ;
        str[0] = '$' ; str[1] = '#' ;
        rep(i , 0 , len){
            str[i*2 + 2] = ch[i] ;
            str[i*2 + 3] = '#' ;
        }
        len = len << 1 ; len += 2 ; str[len] = 0 ;
        solve() ;
    }
    return 0 ;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值