HDU-3068-最长回文

这题好像只能用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);
    }
}
原文地址:https://www.cnblogs.com/xyqxyq/p/10366584.html