hdu 3068 最长回文(manacher)

题意:给定一串字符串,求其中最长回文的长度,输出之。

 

题解:刚看到这题,以为用扩展kmp就可以解决,扩展kmp的时间复杂度是O(n*logn),对于此题,n最大为11W,但是测试用例有120组。一组测试用例大概就一两百万的处理量的样子,而一共有120组,那么总的处理就可能达到几个亿了,估计这样的复杂度是吃不消的。而且也看到别人说这题卡logn的时间(他是用后缀数组求的),所以必须要有一个O(n)的算法,很巧的是,我在我一个同学那里看到了这样的一个算法,很不错,名字就叫做manacher算法。

 

其实manacher算法还是蛮容易懂的,利用的原理和扩展kmp有点相似,所以看一下就立马懂了。下面我讲一点重要的部分。

manacher算法的重点是对已经处理的点的最长回文的值的有效利用,

这个算法是从头开始一个一个的计算每一点的最长回文的长度,

而在最开始的时候要对字符串进行一个处理,就是在每两个字符的中间加一个字符,这个字符要是字符串中所没有的,在字符串的两端也要加。加这个的原因在于,我们要讲奇数长度回文的字符串和偶数长度回文的字符串统一处理,这样就可以再线性时间内算出最长回文的长度了。

对于我们已经处理的字符,我们知道了他们的最长回文长度,我们要记下两个值,一个是最长回文所能到达的最右边的下标mx,以及那个回文的字符的下标id。

当我们处理第i个字符时,会出现两种情况,一种是i>mx,这时的i是没处理没比较过的,所以令其最长长度为i,然后为两边一个一个的比较。

另一种是i<mx,这时我们知道以id为对称轴的i的左边的下标是j = 2*id - i,其最长回文是p[j];

则有两种情况:

(2*id - mx )    (j - p[j])    j   (j + p[j])    id                    i       mx

从左到右,我们可以看到,以为id这个点的回文可以到达mx,所以id到mx这段的字符和2*id-mx到id是相同的,只不过方向相反。

现在我们可以看到对于j点,在字符串中他的字符肯定是和i点的字符时一样的(根据id对称)。

那么i的回文也会和j的回文有一部分类似。

当j-p[j] >= 2*id - mx时,也就是对于j点的回文长度他是没有超过id的回文长度的范围的,所以关于轴对称的i点他的回文长度就等于就j点的回文长度。

当j-p[j] < 2*id - mx 时,根据轴对称,我们知道i到mx这一段绝对是包括在i的回文里面的,所以长度要加上这个值,然后再往后两边依次逐个对比,相等长度就加1,直到不等。

 然后跟新id和mx就行了。

最后我们求出最大的一个p[i]-1就可以了。因为两边加了一个字符要减去1.

在对原字符串处理时,应在最开头的位置加一个特殊的字符,是后面不可能出现的字符。这样做的原因是防止对比时越界。

 

 

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX_ = 110010;

char str[MAX_*2], str1[MAX_];
int p[MAX_*2];

int main() {
    int mx,id;
    while(~scanf("%s",str1)) {
        int t = 1,len = strlen(str1);
        str[0] = '$';
        for(int i = 0; i < len; ) {
            if(t & 1)
                str[t++] = '#';
            else {
                str[t++] = str1[i++];
            }
        }
        str[t++] = '#';
        str[t] = '\0';
        mx = 0;
        for(int i = 1; i < t; i++) {
            if(i < mx) {
                p[i] = min(p[id*2-i],mx-i);
            } else p[i] = 1;
            for(; str[p[i]+i] == str[i-p[i]]; p[i]++);

            if(p[i] + i > mx) {
                mx = p[i] + i;
                id = i;
            }
        }
        int ans = -1;
        for(int i = 1; i < t; i++){
            if(p[i] - 1 > ans){
                ans = p[i] - 1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


 

 

你可能感兴趣的:(hdu 3068 最长回文(manacher))