Manacher 马拉车算法

Manacher算法

一个处理字符串中极长回文串的字符串算法


首先 , 我们可以想一想比较暴力的做法

无非就是枚举回文串的中点

我们有两种回文串 , 一种的长度是奇数的 , 一种长度是偶数

长度是奇数的就是中点是一个字符 , 但是长度是偶数呐

那样中点就是两个字符的中间空白处 , 但是这样处理起来好像有点麻烦

所以我们不妨在每两个字符的中间都插入一个不存在原字符串的一个字符

这样原回文串还是回文串 , 非回文串依旧还不是回文串

然后向外扩展 , 这样显然是的算法

那么Manacher算法 , 就是优化这个暴力的算法


 

首先, 我们也是从左向右依次枚举每个中点

我们不妨将以 i 点为中点的极长回文串的长度记为P_i

同时 , 我们将右端点最靠右的回文串的右端点记为 right , 而中点记为 plc

如果我们要处理 i 为中点的回文串 , 那么很显然前i - 1 为中点的回文串都已经处理完了

那么我们来判断一下 , 如果当前点在 right 的左侧 , 那么很显然 plc * 2 - i 的点是与 i 对应的 , 我们不妨设他为 s

 

 对于这张图, 我们的 i 是黄色点 , 绿色点是 plc , 紫色点是黄点所对应的点s , 括号里的字符串就是当前所处理到的右端点最靠右的回文串

这个时候就需要分类讨论了

第一种情况

这种情况答案很简单

因为她们处于同一个回文串的不同侧

那么第二种情况

那么我们就要开始暴力枚举了 , 从 right 点开始 , 一直向外扩展


这样的话 , 每个点最多只会被遍历到一次

所以复杂度 O(n)

例题:【模板】manacher算法

代码:

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<iostream>
 6 #include<algorithm>
 7 #define APART puts("----------------------")
 8 #define debug 1
 9 #define FILETEST 0
10 #define inf 30000010
11 #define ll long long
12 #define ha 998244353
13 #define INF 0x7fffffff
14 #define INF_T 9223372036854775807
15 #define DEBUG printf("%s %d\n",__FUNCTION__,__LINE__)
16 
17 namespace chino{
18 
19 inline void setting(){
20 #if FILETEST
21     freopen("test.in", "r", stdin);
22     freopen("test.me.out", "w", stdout);
23 #endif
24     return;
25 }
26 
27 inline int read(){
28     char c = getchar(), up = c; int num = 0;
29     for(; c < '0' || c > '9'; up = c, c = getchar());
30     for(; c >= '0' && c <= '9'; num = (num << 3) + (num << 1) + (c ^ '0'), c = getchar());
31     return  up == '-' ? -num : num;
32 }
33 
34 int len;
35 int right, plc;
36 int p[inf];
37 char str[inf];
38 
39 inline void Init(){
40     str[0] = '\a';
41     str[1] = '#';
42     len = 1;
43     char c = getchar();
44     while(c >= 'a' && c <= 'z'){
45         str[++len] = c;
46         str[++len] = '#';
47         c = getchar();
48     }
49     return;
50 }
51 
52 inline int Manacher(char *str){
53     int ansend = 0;
54     for(int i = 1; i <= len; i++){
55         if(right > i)
56             p[i] = std::min(p[(plc << 1) - i], right - i);
57         else p[i] = 1;
58         while(str[i + p[i]] == str[i - p[i]])
59             ++p[i];
60         p[i] + i > right ? right = p[plc = i] + i : 0;
61         ansend = std::max(ansend, p[i]);
62     }
63     return ansend - 1;
64 }
65 
66 inline int main(){
67     Init();
68     printf("%d\n", Manacher(str));
69     return 0;
70 }
71 
72 }//namespace chino
73 
74 int main(){return chino::main();}

 

posted @ 2019-09-01 16:31  ChiaroShiro  阅读(154)  评论(0编辑  收藏  举报