Manacher 算法(hdu 3068 && hdu 3294)

  今天打算补前晚 BC 的第二题,发现要用到能在 O(n) 时间求最大回文子串长度的 Manacher 算法,第一次听,于是便去百度了下,看了大半天,总算能看懂了其思想,至于他给出的代码模板我没能完全看懂,只好自己试着实现,发现理解了思想后还是能实现出来的,用自己的风格去写更好理解,先附上讲解 Manacher 算法的几个链接:

  Manacher算法--O(n)回文子串算法 (我就是看这个理解的~)

  Manacher算法处理字符串回文

  hdu3068之manacher算法+详解

  浅谈manacher算法

  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 }
View Code

  还有一题也是需要用到这个算法的,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 }
View Code

 

posted @ 2015-08-03 18:15  Newdawn_ALM  阅读(269)  评论(0编辑  收藏  举报