HDU3068 最长回文 MANACHER+回文串
Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
回文就是正反读都是一样的字符串,如aba, abba等
Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
两组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 }