现在讲的也是一种处理字符串的方法,叫做Manacher,有点像“马拉车”
1179: [视频]【Manacher】最长回文子串
时间限制: 1 Sec 内存限制: 128 MB
提交: 209 解决: 120
[提交] [状态] [讨论版] [命题人:admin]题目描述
【题意】
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文子串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
【输入格式】
输入有多组数据,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
每组数据之间由空行隔开(该空行不用处理)
字符串长度len<=100000
【输出格式】
每一行一个整数x,对应一组数据,表示该组数据的字符串中所包含的最长回文子串长度.
【样例输入】
aaaa
abab
【样例输出】
4 3
朴素做法:3重for循环:第一重枚举开头,第二重枚举结尾,第三重用来判断是不是回文串,记录答案更新最大值
但是我们的数据范围是10万,三重for循环是不可能不炸的
然后就要用到我们的Manacher,但是这道题也可以用后缀数组来做,不过Manacher更简单方便,下面我们来看一下“马拉车”
a w e r s e s s a
# a # w # c # r # s # e # s # s # a #
我们看到一个串,要判断他的串时,我们就要分他的长度是单数还是复数的情况,为什么呢?因为我们的回文是有两种情况的,单数和偶数的情况是不一样的
(非常不方便)
所以我们在头和尾,以及两个字符之间加一个#号,(其他的也可以,只要是字符串当中没有出现过的就可以)
那么经过这个操作之后,不过原来是偶数还是奇数,加上#号之后都会变成奇数,这样我们处理起来就会方便很多
# a # w # c # r # s # e # s # s # a #
p[ ]= 1 2 1 2 1 2 1 2 1 2 1 4 1 2 3 2 1 2 1
首先#号是自己一个,所以是1
a是与# a #构成,所以是2,剩下的都是同样的道理
一直到e的出现,他是 # s # e # s # 所以是4,这样我们就知道了p数组的实现了p[i]表示以i为中心构成的最长回文字串的结尾与i的距离(结果要+1,因为算上了i)
然后我们看回e,是第12个字符,p[12]=4,然后他的这个回文串的长度是7,向右延伸4个,向左延伸4个,重复算了1个,所以就是4*2-1=7(包括#号)
然后因为答案是不算#号的,所以真正的是 (7-1)/2=3
因为# s # e # s #头尾都有#号,所以删掉一个之后,就是一个#号与一个数字搭配,这样就可以直接除以2算出字串的真正长度所以我们发现最长回文字串的长度就是
=(p[i]*2-1-1)/2
=(p[i]*2-2)/2
=p[i]-1 (一定要记住这个最后的变式,特别重要)
看了我前面博客的一定都知道,这是我目前在讲字符串专题的时候用到的图是最简单的了
然后我们看到图
首先字符串的长度是1~len
l,r表示之前计算的最长回文字串的开头和结尾,r代表结尾所到达的最大的地方,l代表开头
pos代表回文串的中心分类讨论 (有两种情况)
1.i<r
我们可以发现p[i]=p[j]
因为l~r是一个回文串,那么我们就可以直接得出p[i]=p[j],那么p[i]就可以直接继承了2.i>=r
这个时候就不能直接继承了,因为我们不知道i后面的p数组是怎样的,所以我们只能直接暴力求出p[i]
然后j这个位置我们可以p[i]=min(p[j],r-i)
因为有时候i所构成的最长回文字串是比r还要长的,那么这个时候我们就只能继承i~r这个长度,不能继承到后面,因为我们根本不知道绿色格子的长度是不是相等的,所以我们不能继承到后面
有因为i与j对称,所以直接p[j]就可以了然后j的话,我们可以用 pos-(i-pos)=pos*2-i
因为i到pos的距离等于j到pos的距离,因为i是以pos为中心和j对称,也就是j是i的对称点所以我们可以直接用pos*2-i=j来确定j的位置
然后Manacher讲到这里的时候,我们的这道题就已经解决的差不多了,剩下的就是看代码的实现
(注释版 因为这个相对于EXKMP要好理解很多,所以可以先试着自己打一遍,再看注释深刻理解)

1 #include<cstdio>
2 #include<cstring>
3 #include<cstdlib>
4 #include<algorithm>
5 #include<cmath>
6 #include<iostream>
7 using namespace std;
8 char s[200010],tt[200010];/*tt表示新字符串,s表示原字符串*/
9 int p[200010],ans;
10 void Manacher()
11 {
12 int len=strlen(s+1);
13 for(int i=1;i<=len;i++) tt[2*i-1]='#',tt[2*i]=s[i];/*构造带#新字符串*/
14 len=len*2+1;/*新字符串的长度*/
15 tt[len]='#';/*最后面也是#号*/
16 int pos=0,r=0;
17 /*r为之前计算的最长回文字串的结尾所到达的最大地方(右端点的最大值)
18 pos表示r所在最长回文字串的中心*/
19 for(int i=1;i<=len;i++)
20 {
21 int j=2*pos-i;/*j为i的对称点*/
22 if(i<=r) p[i]=min(p[j],r-i);
23 /*
24 如果i<=R的话,分两种情况
25 第一种情况p[j]>R-i时,表示j对称点的最长回文子串已经越出R的界限了
26 这时,因为我们不确定大于R时的情况,所以p[i]暂时等于R-i
27 第二种情况p[j]<=R-i时,那就可以直接继承p[j]得p[i]=p[j]
28 综合以上两种情况,我们可以用p[i]=min(p[j],R-i)来归纳
29 */
30 else p[i]=1;/*不然就为1,就是自己成为一个回文串*/
31 while(1<=i-p[i] && i+p[i]<=len && tt[i-p[i]]==tt[i+p[i]]) p[i]++;
32 /*i-p[i]就是i的前面几个位置>=1,而且i的右边几个位置<=len,也就是说我构成的p[i]长度的是不会不合法的,
33 如果i-p[i]的字符等于i+p[i]的话,也就是说我的回文是成立的,变长了,那么长度增加*/
34 if(i+p[i]>r)/*右端点比r大*/
35 {
36 pos=i;/*把pos定为i*/
37 r=i+p[i];/*更新r*/
38 }
39 }
40 ans=0;
41 for(int i=1;i<=len;i++) ans=max(ans,p[i]-1);/*p[i]-1就是真正回文串的长度
42 那么有#号怎么办,有#号也没有关系,因为p[i]-1终究是没有#号的,其实这个我们直接推出来的过程,而且记录的是最大值,
43 所以的话,根本不会出现这种情况*/
44 /*
45 首先当前的最长回文子串长度为2*p[i]-1
46 因为我们得到的p数组是在加了#号后的字符串上操作的,所以我们要对答案进行处理
47 因为#号处于首尾和每个字符之间,所以我们就可以保证所得出的最长回文子串的首尾都为#
48 这时我们可以得出不带#号的回文串的长度为(2*p[i]-1-1)/2=p[i]-1
49 所以真正的最长回文子串就是p[i]-1
50 ans记录最长的回文子串长度
51 */
52 }
53 int main()
54 {
55 while(scanf("%s",s+1)!=EOF)
56 {
57 Manacher();
58 printf("%d\n",ans);
59 }
60 return 0;
61 }

1 #include<cstdio>
2 #include<cstring>
3 #include<cstdlib>
4 #include<algorithm>
5 #include<cmath>
6 #include<iostream>
7 using namespace std;
8 char s[200010],tt[200010];
9 int p[200010],ans;
10 void Manacher()
11 {
12 int len=strlen(s+1);
13 for(int i=1;i<=len;i++) tt[2*i-1]='#',tt[2*i]=s[i];
14 len=len*2+1;
15 tt[len]='#';
16 int pos=0,r=0;
17 for(int i=1;i<=len;i++)
18 {
19 int j=2*pos-i;
20 if(i<=r) p[i]=min(p[j],r-i);
21 else p[i]=1;
22 while(1<=i-p[i] && i+p[i]<=len && tt[i-p[i]]==tt[i+p[i]]) p[i]++;
23 if(i+p[i]>r)
24 {
25 pos=i;
26 r=i+p[i];
27 }
28 }
29 ans=0;
30 for(int i=1;i<=len;i++) ans=max(ans,p[i]-1);
31 }
32 int main()
33 {
34 while(scanf("%s",s+1)!=EOF)
35 {
36 Manacher();
37 printf("%d\n",ans);
38 }
39 return 0;
40 }

