Manacher 算法(hdu 3068 && hdu 3294)
今天打算补前晚 BC 的第二题,发现要用到能在 O(n) 时间求最大回文子串长度的 Manacher 算法,第一次听,于是便去百度了下,看了大半天,总算能看懂了其思想,至于他给出的代码模板我没能完全看懂,只好自己试着实现,发现理解了思想后还是能实现出来的,用自己的风格去写更好理解,先附上讲解 Manacher 算法的几个链接:
Manacher算法--O(n)回文子串算法 (我就是看这个理解的~)
hdu 3068 正好是裸题,我便试着写下,我是这样子构造新串的:
hdu 3068 代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 110005; 6 7 // str 为原串, s 为新串 8 char str[N], s[N << 1]; 9 int p[N << 1]; 10 // p[i] 表示以 s[i] 为中心时的回文半径,不包括 p[i] 11 // 即若 s[i - 1] != s[i + 1] 时,p[i] = 0; 12 13 int main() { 14 while(~scanf("%s",str)) { 15 int n = strlen(str); 16 s[0] = '$'; // 构造新串 17 s[1] = '#'; 18 for(int i = 0; i < n; ++i) { 19 s[i * 2 + 2] = str[i]; // 下标要处理好 20 s[i * 2 + 3] = '#'; 21 } 22 n = n * 2 + 2; // 更新新串的长度 23 s[n] = '\0'; // 最后的结束符别忘了 24 25 // right 记录的是在 i 之前的回文串中,某个回文串延伸至最右端的位置 26 // id 就是该回文串的下标(注意都是在新串中的) 27 int right = 0, id = 0; 28 p[0] = 0; 29 // 因为是 s[0] == '$',作为特殊标记,左右两边都没有相等的,所以初始化为 0, 30 // 同理 right 一开始能延伸到的位置就是 s[0] 的位置,也就是 0,id 当然也为 0 31 32 // 主算法要开始了 33 for(int i = 1; i < n; ++i) { 34 if(right > i) 35 p[i] = min(p[2 * id - i], right - i); 36 else p[i] = 0; 37 while(s[i + p[i] + 1] == s[i - p[i] - 1]) ++p[i]; 38 if(i + p[i] > right) { 39 right = i + p[i]; 40 id = i; 41 } 42 } 43 44 // printf("\n下标: "); 45 // for(int i = 0; i <= n; ++i) 46 // printf("%d ",i); 47 // puts(""); 48 // printf("新串: "); 49 // for(int i = 0; i < n; ++i) 50 // printf("%c ",s[i]); 51 // printf(" \\0\np[i]: "); 52 // for(int i = 0; i < n; ++i) 53 // printf("%d ",p[i]); 54 // puts(""); 55 56 int ans = 0; 57 for(int i = 1; i < n; ++i) 58 ans = max(ans, p[i]); // p[i] 就是原串中的回文长度, 无须作任何 +1、-1 59 printf("%d\n",ans); 60 } 61 return 0; 62 }
还有一题也是需要用到这个算法的,hdu 3294,只是对于最后的结果输出需要处理一下,恶心的模拟,直接贴代码了:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 200005; 6 7 char str[N], s[N << 1]; 8 int p[N << 1]; 9 10 int main() { 11 while(gets(str)) { 12 int n = strlen(str); 13 s[0] = '$'; 14 s[1] = '#'; 15 for(int i = 2; i < n; ++i) { 16 s[i * 2 - 2] = str[i]; 17 s[i * 2 - 1] = '#'; 18 } 19 n = n * 2 - 2; 20 s[n] = '\0'; 21 22 int right = 0, id = 0; 23 p[0] = 0; 24 for(int i = 1; i < n; ++i) { 25 if(right > i) 26 p[i] = min(p[id * 2 - i], right - i); 27 else p[i] = 0; 28 while(s[i + p[i] + 1] == s[i - p[i] - 1]) ++p[i]; 29 if(i + p[i] > right) { 30 right = i + p[i]; 31 id = i; 32 } 33 } 34 int Max = 0, mid; 35 for(int i = 1; i < n; ++i) { 36 if(p[i] > Max) { 37 Max = p[i]; 38 mid = i; 39 } 40 } 41 if(Max == 1) { 42 puts("No solution!"); 43 continue; 44 } 45 46 int strid = (mid - Max + 1) / 2 + 1; 47 printf("%d %d\n", strid - 2, strid - 2 + Max - 1); 48 49 for(int i = 0; i < Max; ++i) { 50 char ch = str[strid + i] + ('a'- str[0]); 51 if(ch < 'a') ch = 'z' + 1 - ('a' - ch); 52 else if(ch > 'z') ch = 'a' - 1 + (ch - 'z'); 53 printf("%c",ch); 54 } 55 puts(""); 56 } 57 return 0; 58 }