这题好像只能用mancher算法解决,速成马拉车算法。
这个算法的核心思想就是,如果当前要维护的下标i小于上一次经维护后的max值,我们就取上一次得到的对称位置的回文半径。
这个回文半径就是min(p[ id * 2 - i] , max-i),它的意思就代表了,我们先取其中最小的一个半径,然后我们之后再暴力向后拓展。
因为我们下一步是暴力,所以我们上一步取一个小值即可。括号中的两个表达式的意思是,它的对称位置 由 中心位置
id-(i-id) 得到,所以就取它对称位置的回文半径值,要么就是上次拓展的最大位置减去i值,它们两个中取一个最小值,
这样我们就不必暴搜之前的那么多次了。我们只用搜索之后的就可了。
但是在我们维护之前,我们可以看到,如果我们维护奇数长度的子串的时候,还是很方便的,所以当我们维护偶数长度的时候,我们就把偶数变奇数即可。
我们在每次字符之间加上一个#,这样的话,如果是偶数长度,中间就有奇数个间隙,左右两边共有两个#,偶数加偶数加奇数,就是奇数。对于奇数长度同理。
然后我们求的这个数组p里面求的其实是半径值,而我们之前把原子串扩展了1倍,所以而我们求的半径值减一就是原子串的直径。
这就是马拉车算法的思路,其实和KMP还是蛮像的。
#include <cstdio>
#include <cstring>
const int maxn = 3e5;
char s[maxn], str[maxn];
int len1, len2, p[maxn], ans;
int min(int x,int y)
{
return x < y ? x : y;
}
void init()
{
memset(p, 0, sizeof(p));
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] = '*';
}
void Manacher()
{
int id = 0, mx = 0;
for (int i = 1; i < len2;i++) {
if (i<mx)
p[i] = min(p[2 * id - i], mx - i);
else
p[i] = 1;
//直接取出它的最小回文半径,然后我们再拓展它
while (str[i-p[i]]==str[i+p[i]])
p[i]++;
if (p[i]+i>mx) {
mx = p[i] + i;
id = i;
}
}
}
int main()
{
while (scanf("%s",s)!=EOF) {
len1 = strlen(s);
init();
Manacher();
ans = 0;
for (int i = 1; i < len2;i++) {
if (p[i]>ans)
ans = p[i];
}
printf("%d
", ans-1);
}
}