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 }
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 }
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 }
作者:萌萌的美男子
出处:http://www.cnblogs.com/ivan-count/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://www.cnblogs.com/ivan-count/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号