马拉车(Manacher)算法是一种时间复杂度为O(n)的求字符串中的最大回文子串的算法,
他很好的利用了回文串是关于中间字符对称的特点进行了剪枝操作,把时间复杂度优化到了O(n),并且它可以无差别处理奇偶字符串。
墙裂建议配合B站视频食用
UESTCACM 每周算法讲堂 manacher算法1(原理和思想)
UESTCACM 每周算法讲堂 manacher算法2(代码与实现)
我比较推荐看下面那个代码与实现,对着题边敲边讲,还是挺容易理解,如果看了这两个视频还是不理解的话,那就看看这个博客吧 —> Manacher Algorithm 马拉车算法(感谢博主) <— 或者也可以先看这个博客再去看视频。
其实最关键的就是下面的这些
图一
图二
一、mx > i
假设当前遍历到字符串的位置i,由于在遍历到id位置的时候已知最大回文子串,位置i还在上一个最大回文子串的范围内,所以可以利用其镜像认为,位置i以id为中心镜像到另一边的位置j是对等的。 在mx>i的条件下,又分为以下两种情况:
1. mx - i > p[j] (图1)
此时,以j为中心的回文子串包含在以id为中心的回文子串内,由于i和j位置对等,所以以i为中心的回文子串包含在以id为中心的回文子串内,所以p[i] = p[j] = p[2 * id - i]。
2. mx - i <= p[j] (图2)
此时,以j为中心的回文子串超过了以id为中心的回文子串边界,但是由于i和j位置对等,绿框部分还是相同的。所以其向右延伸的范围最大就是mx-i,剩下超过的部分谁也不能保证是否一致,只能通过循环对比判断,所以p[i] = mx - i。
二、mx < i
此时镜像对预判位置起不到作用,只能从长度为1开始对比,所以p[i] = 1。
然后我就直接附上板子了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e3;
char s[maxn],str[maxn];
int len1,len2,p[maxn];
void manacher()
{
int id = 0,mx = 0;
for(int i = 1; i < len2; i++)
{
if(mx > i) p[i] = min(p[2*id-i],mx-i);
else p[i] = 1;
for(; str[i+p[i]] == str[i-p[i]]; p[i]++);
if(p[i] + i > mx)
{
mx = p[i]+i;
id = i;
}
}
}
void init()
{
str[0] = '$';
str[1] = '#';
for(int i = 0; i < len1; i++)
{
str[i*2+2] = s[i];
str[i*2+3] = '#';
}
len2 = len1*2+2;
str[len2] = '*';
}
int main()
{
int n;
cin >> n;
while(n--)
{
cin >> s;
len1 = strlen(s);
init();
manacher();
int ans = 0;
for(int i = 0; i < len2; i++)
{
ans = max(ans,p[i]);
}
cout << ans-1 << endl;
}
return 0;
}