马拉车算法 Manacher + 例题: hihocoder #1032 最长回文子串

【马拉车Manacher算法】

引入:

计算字符串的最长回文字串,最朴素的算法就是枚举字符串的每一个子串,并判断这个子串是否为回文串,这个算法的时间复杂度为O(n^3),显然无法令人满意。稍微优化的算法是枚举回文串的中点,这里要分为两种情况,一种是回文串长度是奇数的情况,另一种是回文串长度是偶数的情况。枚举中点再判断是否是回文串,这样能把算法的时间复杂度降为O(n^2),但是当n比较大的时候仍然无法令人满意。而Manacher算法可以在O(n)时间复杂度内求出一个字符串的最长回文字串,达到了理论上的下界。

实现:

Manacher算法是基于中心扩散法,采用以空间换时间的思路,将求最长回文子串的复杂度降到O(n)的巧妙方法。

思路:为了将长度为奇数的回文串和长度为偶数的回文串一起考虑,在原字符串的每个相邻两个字符中间插入一个分隔符,同时在首尾也要添加一个分隔符,分隔符的要求是不在原串中出现。用一个数组Len[i]表示以字符S[i]为中心的最长回文字串的最右字符到S[i]的长度,比如以S[i]为中心的最长回文字串是S[l, r],那么Len[i] = r - i + 1。Len数组有一个性质,那就是Len[i] - 1就是该回文子串在原字符串S中的长度。

Len数组的计算:

设right为以字符S[i]为中心的最长回文字串的最右字符的位置,令pos为字符S[i]的位置i,i从左往右匹配。

存在两种情况:①当i < right时,我们把以pos为对称中心的与i对称的点即 j = 2 * pos - i。如果以j为中心的最长回文串在以pos为中心的最长回文串里,根据对称性得到 Len[i] = Len[j];如果以j为中心的最长回文串不在以pos为中心的最长回文串里,说明超出这个范围了,那么根据对称性得到Len[i] = right - i。综上所述,Len[i] = min(right - i, Len[j])。

当i>=right时,说明以T[i]为中心的回文串一个都没有进行匹配,所以Len[i]=1,需要从头开始一个个往下匹配。

时间复杂度O(n),空间复杂度O(n)。

【题目】

最长回文子串

【代码】

#include 
using namespace std;
const int maxn = 1e6 + 10;
char s[maxn];
char t[maxn<<1];
int Len[maxn<<1];
int len;
void init()
{
    int k=0;
    t[k++]='#';
    for(int i = 0; i < len; i++){
        t[k++] = s[i];
        t[k++] = '#';
    }
    len = k;
}

int Manacher()
{
    int sum = 0, right = 0, pos = 0;
    for(int i = 1; i < len; i++){
        if(i < right){
            Len[i] = min(right - i, Len[2 * pos - i]);
        }
        else{
            Len[i]=1;
        }
        while(t[i - Len[i]] == t[i + Len[i]]){
            Len[i]++;
        }
        if(Len[i] + i > right){
            right = Len[i] + i;
            pos = i;
            sum = max(sum, Len[i]);
        }
    }
    return sum - 1;
}
int main()
{
    int n; scanf("%d", &n);
    while(n--){
        scanf("%s", s);
        len = strlen(s);
        init();
        int ans = Manacher();
        printf("%d\n", ans);
    }
    return 0;
}

 

你可能感兴趣的:(板子)