转载

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

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

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

思路:

  • 枚举子串的两个端点,根据回文串的定义来判断其是否是回文串并更新答案,复杂度O(N 3 )。
  • 枚举回文串的对称轴i,以及回文半径r,由i和r可确定一个子串,然后暴力判断即可。复杂度O(N 2 )。
  • 在上一步的基础上,改进判断子串是否是回文串的算法。记f i (r)=(bool)以i为对称轴半径为r的子串是回文串,f i (r)的值域为{0, 1},显然f i (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; } 
正文到此结束
Loading...