Manacher专题

 1、POJ 3974 Palindrome

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

  思路:Manacher模板。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 1000010;
 5 char s[maxn];
 6 char tmp[2 * maxn + 3];
 7 int p[2 * maxn + 2];
 8 int Manacher(char *s,char *tmp,int *p, int &start)
 9 {////s原字符串,tmp转换后的字符串,p记录长度
10     //char s[maxn];
11     //char tmp[2 * maxn + 3];
12     //int p[2 * maxn + 2];
13     //将所有可能的奇数 / 偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#
14     int n = strlen(s);
15     for (int i = 0; i < 2 * n + 2; i++)
16     {
17         if (i == 0) tmp[i] = '$';
18         else if (i % 2) tmp[i] = '#';
19         else tmp[i] = s[i / 2 - 1];
20     }
21     tmp[2 * n + 2] = '\0';
22 
23     memset(p, 0, sizeof(p));
24 
25     int len = 2 * n + 2;
26     int i = 0, mx = 0, id, ans = 1;//id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界,即回文串最右边字符的最大值
27     start = 0;
28     for (i = 1; i < len; i++)
29     {
30         if (mx > i) p[i] = min(p[2 * id - i], mx - i);//在Len[j]和mx-i中取个小
31         else p[i] = 1;//如果i>=mx,要从头开始匹配
32         while(tmp[i + p[i]] == tmp[i - p[i]])p[i]++;
33         if (p[i] + i > mx) mx = p[i] + i, id = i;//若新计算的回文串右端点位置大于mx,要更新id和mx的值
34         if (p[i] - 1 > ans)//返回Len[i]中的最大值-1即为原串的最长回文子串额长度 
35         {
36             ans = p[i] - 1;
37             start = (i - (p[i] - 2)) / 2 - 1;
38         }
39     }
40     return ans;
41 }
42 
43 int main()
44 {
45     int t = 1;
46     while (~scanf("%s", s))
47     {
48         if (s[0] == 'E')break;
49         int st = 0;
50         int ans = Manacher(s, tmp, p, st);
51         printf("Case %d: %d\n",t++, ans);
52     }
53     return 0;
54 }
View Code

2、HDU 4513 吉哥系列故事――完美队形II

  题意:求最长回文数字子串,且左右向中间递增。

  思路:manacher基础算法上加个判定条件:即向两边延伸的大小限制。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 1000010;
 5 int s[maxn];
 6 int tmp[2 * maxn + 3];
 7 int p[2 * maxn + 2];
 8 int n;
 9 int Manacher(int *s, int *tmp, int *p, int &start)
10 {////s原字符串,tmp转换后的字符串,p记录长度
11  //char s[maxn];
12  //char tmp[2 * maxn + 3];
13  //int p[2 * maxn + 2];
14  //将所有可能的奇数 / 偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#
15     for (int i = 0; i < 2 * n + 2; i++)
16     {
17         if (i == 0) tmp[i] = -1;
18         else if (i % 2) tmp[i] =0;
19         else tmp[i] = s[i / 2 - 1];
20     }
21     //tmp[2 * n + 2] = '\0';
22 
23     memset(p, 0, sizeof(p));
24 
25     int len = 2 * n + 2;
26     int i = 0, mx = 0, id, ans = 1;//id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界,即回文串最右边字符的最大值
27     start = 0;
28     for (i = 1; i < len; i++)
29     {
30         if (mx > i) p[i] = min(p[2 * id - i], mx - i);//在Len[j]和mx-i中取个小
31         else p[i] = 1;//如果i>=mx,要从头开始匹配
32         while (tmp[i + p[i]] == tmp[i - p[i]]&&tmp[i+p[i]]<=tmp[i+p[i]-2])p[i]++;
33         if (p[i] + i > mx) mx = p[i] + i, id = i;//若新计算的回文串右端点位置大于mx,要更新id和mx的值
34         if (p[i] - 1 > ans)//返回Len[i]中的最大值-1即为原串的最长回文子串额长度 
35         {
36             ans = p[i] - 1;
37             start = (i - (p[i] - 2)) / 2 - 1;
38         }
39     }
40     return ans;
41 }
42 int main()
43 {
44     int T;
45     scanf("%d", &T);
46     while (T--)
47     {
48         scanf("%d", &n);
49         for (int i = 0; i < n; i++) scanf("%d", &s[i]);
50         int st;
51         int ans = Manacher(s, tmp, p, st);
52         printf("%d\n", ans);
53     }
54 
55     return 0;
56 }
View Code

 3、hdu 3294 Girls' research

  题意:给出一个字符串,然后给出转换字符C(表示C代表真正的‘a’),然后求转换后的字符串的大于等于2的最长回文子串。

  思路:其实可以先求处原字符串的最长回文子串,再转换输出即可。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 1000010;
 5 char s[maxn];
 6 char tmp[2 * maxn + 3];
 7 int p[2 * maxn + 2];
 8 int Manacher(char *s, char *tmp, int *p, int &start)
 9 {////s原字符串,tmp转换后的字符串,p记录以tmp[i]为中心的最长回文子串的最右端到tmp[i]的位置的长度
10  //char s[maxn];
11  //char tmp[2 * maxn + 3];
12  //int p[2 * maxn + 2];
13  //将所有可能的奇数 / 偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#
14     int n = strlen(s);
15     for (int i = 0; i < 2 * n + 2; i++)
16     {
17         if (i == 0) tmp[i] = '$';
18         else if (i % 2) tmp[i] = '#';
19         else tmp[i] = s[i / 2 - 1];
20     }
21     tmp[2 * n + 2] = '\0';
22 
23     memset(p, 0, sizeof(p));
24     int len = 2 * n + 2;
25     int i = 0, mx = 0, id, ans = 1;//id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界,即回文串最右边字符的最大值
26     start = 0;
27     for (i = 1; i < len; i++)
28     {
29         if (mx > i) p[i] = min(p[2 * id - i], mx - i);//在Len[j]和mx-i中取个小
30         else p[i] = 1;//如果i>=mx,要从头开始匹配
31         while (tmp[i + p[i]] == tmp[i - p[i]])p[i]++;
32         if (p[i] + i > mx) mx = p[i] + i, id = i;//若新计算的回文串右端点位置大于mx,要更新id和mx的值
33         if (p[i] - 1 > ans||(p[i]-1==ans&& (i - (p[i] - 2)) / 2 - 1<start))//返回Len[i]中的最大值-1即为原串的最长回文子串额长度 
34         {
35             ans = p[i] - 1;
36             start = (i - (p[i] - 2)) / 2 - 1;
37         }
38     }
39     return ans;
40 }
41 
42 int main()
43 {
44     char c;
45     while (cin>>c)
46     {
47         scanf("%s", s);
48         int st = 0;
49         int ans = Manacher(s, tmp, p, st);
50         if (ans >= 2)
51         {
52             printf("%d %d\n", st,st+ans-1);
53             for (int i = st; i < st + ans; i++)printf("%c", ('a'+s[i]+26-c)>'z'? 'a' + s[i]- c: 'a' + s[i] + 26 - c);
54             printf("\n");
55         }
56         else printf("No solution!\n");
57         
58     }
59     return 0;
60 }
View Code

 

posted @ 2017-08-24 10:15  萌萌的美男子  阅读(220)  评论(0)    收藏  举报