[hdu3068 最长回文]Manacher算法,O(N)求最长回文子串

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068

题意:求一个字符串的最长回文子串

思路:

  • 枚举子串的两个端点,根据回文串的定义来判断其是否是回文串并更新答案,复杂度O(N3)。
  • 枚举回文串的对称轴i,以及回文半径r,由i和r可确定一个子串,然后暴力判断即可。复杂度O(N2)。
  • 在上一步的基础上,改进判断子串是否是回文串的算法。记fi(r)=(bool)以i为对称轴半径为r的子串是回文串,fi(r)的值域为{0, 1},显然fi(r)是关于r的单调函数,于是可以二分r,然后用字符串hash在O(1)的时间内判断子串是否是回文串,总复杂度O(NlogN)。
  • 虽然O(NlogN)的复杂度已经非常不错了,但还有线性的算法---Manacher算法。

Manacher算法:维护两个值r和id,r是以前的回文串的最大右边界,id是其对应的下标,如果当前考虑的对称轴i小于等于r,那么从i到r这一段子串是否可以和i左边的子串构成回文串(或者说最长能有多长)其实在之前是已经计算过了的(或者说计算出了一部分),因为将i作关于id的对称点i'=2*id-i,就不难发现i'周围若干字符和i周围若干字符是对应相同的,这是Manacher算法的核心之处,可以用i'的最大回文半径来更新i的最大回文半径,利用这个性质就能做到线性的复杂度。

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#pragma comment(linker, "/STACK:10240000")
#include <bits/stdc++.h>
using namespace std;

#define X                   first
#define Y                   second
#define pb                  push_back
#define mp                  make_pair
#define all(a)              (a).begin(), (a).end()
#define fillchar(a, x)      memset(a, x, sizeof(a))

typedef long long ll;
typedef pair<int, int> pii;

#ifndef ONLINE_JUDGE
namespace Debug {
void print(){cout<<endl;}template<typename T>
void print(const T t){cout<<t<<endl;}template<typename F,typename...R>
void print(const F f,const R...r){cout<<f<<", ";print(r...);}template<typename T>
void print(T*p, T*q){int d=p<q?1:-1;while(p!=q){cout<<*p<<", ";p+=d;}cout<<endl;}
}
#endif // ONLINE_JUDGE
template<typename T>bool umax(T&a, const T&b){return b<=a?false:(a=b,true);}
template<typename T>bool umin(T&a, const T&b){return b>=a?false:(a=b,true);}
/* -------------------------------------------------------------------------------- */

const int maxn = 3e5 + 7;

/** 求字符串每个位置的最大回文半径,在字符串中找最长回文子串 **/
struct Manacher {
    int p[maxn];/** 回文半径 **/
    char s[maxn];
    void init(char str[]) {
        strcpy(s, str);
        int n = strlen(s);
        s[n * 2 + 1] = 0;
        for (int i = n * 2; i; i -= 2) {
            s[i] = '#';
            s[i - 1] = s[i / 2 - 1];
        }
        s[0] = '#';
    }
    /** 求每个点的最大回文半径 **/
    void work() {
        int r = 0, id = 0;
        p[0] = 1;
        for (int i = 1; s[i]; i ++) {
            p[i] = i <= r? min(r - i + 1, p[2 * id - i]) : 1;
            if (p[i] >= r - i + 1) {
                r = (id = i) + p[i] - 1;
                while (2 * i - r - 1 >= 0 && s[r + 1] == s[2 * i - r - 1]) {
                    r ++;
                    p[i] ++;
                }
            }
        }
    }
    /** 求最长回文串的长度 **/
    int solve() {
        work();
        int ans = 1;
        for (int i = 0; s[i]; i ++) {
            ans = max(ans, p[i] - 1);
        }
        return ans;
    }
};
Manacher solver;

char s[maxn];

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    while (~scanf("%s", s)) {
        solver.init(s);
        printf("%d\n", solver.solve());
    }
    return 0;
}
posted @ 2015-09-03 19:08  jklongint  阅读(651)  评论(0编辑  收藏  举报