原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068
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 }
时间: 2024-08-04 14:08:43