最长回文子串-Manacher算法

给你一个字符串,要求出它的最长回文子串的长度。

 Manacher算法

第一步:在字符串所有的空隙中插入一个不会在原串中出现的字符

    aba ———> #a#b#a#
    abba ———> #a#b#b#a#

这样永远不会连续相同的字符,所以会使得所有的串都是奇数长度的。

第二步:定义一个数组RL,RL[i]表示以i为对称点的回文串的半径长度。

  引入两个辅助变量MaxRight以及pos,MaxRight表示当前判断过的所有回文串所能到达的最右侧的下标,pos表示当前最长回文子串的对称中心下标。

如图

然后我们可以从左至右的遍历字符串来求RL[ i ],那么i必定在pos的右侧,因为pos表示i之前的最长回文子串的对称轴的下标。但是此时i在MaxRight的左边还是右边任然是不确定的。

扫描二维码关注公众号,回复: 5616843 查看本文章

1)i在MaxRight的左侧

  此时我们可以找到i关于pos对称的点j,并且以i和以j为对称轴的回文串有一部分也是相同的。

  1.以j为对称轴的回文串的半径比MaxRight到i的距离小,如下图

  这时以i为对称轴的回文串可能更长,那我们可以接着对以i为对称轴,以RL[j]为半径开始扩张进行回文串的判断直到到达边界。

  2.以j为对称轴的回文串的半径比MaxRight到i的距离大

这个时候我们只能确定i到MaxRight的这个部分是回文串,并无法确定MaxRight右侧的情况,那么我们就直接以i为对称轴,以MaxRight-i开始向左右两侧扩张判断回文。

同时我们也要不断的更新MaxRight以及pos的值,应为有可能得到更大的MaxRight

2)当i在MaxRight的右边时

  这个时候无法找i关于pos的对称点,所以此时只能从i开始对左右两边进行判断回文了。

代码实现如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7 #include<set>
 8 #include<map>
 9 #include<iomanip>
10 using namespace std;
11 #define inf 1<<29;
12 
13 string s;
14 char s_new[2000];
15 int p[2000];        //p[i]表示以 i 为中心的最长回文的半径
16 
17 int Init()
18 {
19     int len = s.size();
20     s_new[0] = '$';
21     s_new[1] = '#';
22     int j = 2;
23 
24     for (int i = 0; i < len; i++)
25     {
26         s_new[j++] = s[i];
27         s_new[j++] = '#';
28     }
29 
30     s_new[j] = '\0';  // 别忘了哦
31     
32     return j;  // 返回 s_new 的长度
33 }
34 
35 int Manacher()
36 {
37     int len = Init();  // 取得新字符串长度并完成向 s_new 的转换
38     int max_len = -1;  // 最长回文长度
39 
40     int id;
41     int mx = 0;                    //mx 代表以 id 为中心的最长回文的右边界
42 
43     for (int i = 1; i < len; i++)
44     {
45         if (i < mx)
46             p[i] = min(p[2 * id - i], mx - i);  // 需搞清楚上面那张图含义, mx 和 2*id-i 的含义            2*id-i表示i关于id的对称点(i在id右侧) 
47         else
48             p[i] = 1;
49 
50         while (s_new[i - p[i]] == s_new[i + p[i]])  // 不需边界判断,因为左有'$',右有'\0'
51             p[i]++;
52 
53         // 我们每走一步 i,都要和 mx 比较,我们希望 mx 尽可能的远,这样才能更有机会执行 if (i < mx)这句代码,从而提高效率
54         if (mx < i + p[i])
55         {
56             id = i;
57             mx = i + p[i];
58         }
59 
60         max_len = max(max_len, p[i] - 1);
61     }
62 
63     return max_len;
64 }
65 
66 int main()
67 {
68     while (printf("请输入字符串:\n"))
69     {
70         getline(cin,s);
71         printf("最长回文长度为 %d\n\n", Manacher());
72     }
73     //    cout.setf(ios::fixed);
74 //    cout<<setprecision(4)<<16.0;
75     return 0;
76 }
View Code

原文链接:

最长回文子串——Manacher 算法

猜你喜欢

转载自www.cnblogs.com/yl1918/p/10579539.html