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 ; } 


点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注