HDU3068 最长回文 MANACHER+回文串

Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
 
Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
 
Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
 
Sample Input
aaaa abab
 
Sample Output
4 3
 
该题可以用扩展KMP O(N*logN)进行求解,但在学长那里知道了针对回文子串黑科技一般的O(N)算法,那就是Manacher。
这个算法运用了很多数学上的思维及回文串本身的对称性质。
1.在每两个字符之间填入“#”使串变为偶数长度。
2.对数组每个元素计算以它为中心对称的半径值(p[i])
3.求出最大的p[i]值
4.优化,对每个元素求出p[i]值时,p[i]+i 是之前遍历过的最远的端点时,记录,此后当i小于之前记录的p[i]+i时,直接求最长的半径值。
 
另外要注意的细节问题是,在处理过的数组首尾要加上同内容完全不同的字符如“%” "$"等作为区分,终止,防止越界。
 
 1 #include <stdio.h>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <string.h>
 5 using namespace std;
 6 
 7 
 8 char a[110010], b[220010];
 9 int p[220010];
10 int main()
11 {
12     int n, ma, r, t, x;
13     while(~scanf("%s", &a[1]))
14     {
15         getchar();
16         x=strlen(a+1);
17         b[0]='%';
18         b[1]='#';
19         //printf("~%d",x);
20         for(int i=1; i<=x; i++)
21         {
22             b[2*i]=a[i];
23             b[2*i+1]='#';
24             //printf("%c%c",b[2*i],b[2*i+1]);
25         }
26         b[2*x+2]='$';
27 
28         t=ma=r=0;
29         for(int i=1; i<=2*x+1; i++)
30         {
31             if(r>i)             //如果遍历的该点已经小于最长的半径,则不用考虑该点
32             {
33                 p[i]=min(r-i, p[t-(i-t)]);//优化避免重复匹配
34             }
35             else
36             {
37                 p[i]=1;
38             }
39             while(b[i+p[i]]==b[i-p[i]]) 
40             {
41                 p[i]++;
42             }
43 
44 
45             if(p[i]+i>r) 
46             {
47                 r=p[i]+i;    //记录延伸最长的posiition值
48                 t=i; 
49 } 50 ///// 51 if(ma<p[i]) 52 ma=p[i]; 53 } 54 printf("%d\n", ma-1); 55 } 56 }

 

posted @ 2016-04-02 13:31  Lweleth  阅读(556)  评论(0编辑  收藏  举报