Manacher 学习

先来一段代码压压惊:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1e5+5;

char str[maxn],str2[maxn*2];
int Len[maxn];

int string_init() {
    str2[0] = '$';
    str2[1] = '#';
    int len = 2;
    int length = strlen(str+1);
    for (int i = 1;i<=length;i++) {
        str2[len++] = str[i];
        str2[len++] = '#';
    }
    str2[len-1] = '$';
    str2[len] = '\0';
    return len;
}

int Manacher() {
    int len = string_init();
    int mxlen = -1,maxr = -1,middle = -1;
    for (int i = 1;i < len;i++) {
        if ( maxr < i ) {
            int j = 1,cnt = 1;
            while ( str2[i-j] == str2[i+j] ) j++,cnt += 2;
            Len[i] = cnt; middle = i; maxr = i + j;
            mxlen = max(mxlen,Len[i]);
        } else if ( maxr > i && Len[2*middle-i] == (maxr-i) ) {
            int j = maxr - i,cnt = Len[2*middle-i];
            while ( str2[i-j] == str2[i+j] ) j++,cnt+=2;
            Len[i] = cnt; middle = i; maxr = i + j;
            mxlen = max(mxlen,Len[i]);
        } else if ( maxr >= i ) {
            Len[i] = min(Len[2*middle-i],maxr-i);
        }
    }
    return mxlen;
}

int main() {
    while(~scanf("%s",str+1)){
        printf("%d\n",(Manacher()+1)/2);
    }
    return 0;
}

想像一下对于一个字符串,如何求它的最长的回文子串,暴力的做法就是枚举每一个点然后往两边扩,但是这个算法的时间复杂度是 O ( n 2 ) 时间很不进人意,于是就诞生了Manacher算法

Manacher

开始Manacher之前我们首先要准备两个变量maxr和middle ,maxr 和 middle 依次表示 回文串的最大右边界,和 最大右边界的回文串的中心

(1).

如果maxr小于当前要查找的回文中心,我们就暴力寻找(这一点可以参见,我的Manacher函数中的第一个if)

if ( maxr < i ) {
        int j = 1,cnt = 1;
        while ( str2[i-j] == str2[i+j] ) j++,cnt += 2;
        Len[i] = cnt; middle = i; maxr = i + j;
        mxlen = max(mxlen,Len[i]);
}
(2).

这里写图片描述
请看图片如果当前要寻找的位置 i ,在maxr里面我们可以在回文的对称一边找到 i 并且如果 i 的回文串左边界比maxl大的话,那么我当前位置 i 的回文子串的长度就跟 i 的一样

else if ( maxr >= i ) { // 为什么是min(Len[2*middle-i],maxr-i);呢往下看
    Len[i] = min(Len[2*middle-i],maxr-i);
}
(3).

这里写图片描述
在第二种的基础上,我的 i 的回文串的左边界比maxl要小,但是此时 i 的回文串的长度为 m a x r i 可以证明一下为什么不等于 i 的回文串长度,如果我当前位置的回文子串长度等于 i 的话,那么在maxl和maxr两端的外部是不是还有一段连续相等的串,既然这样的话我的middle得回文串的长度应该更大,可是事实没有更大,因此当前位置的回文子串的长度为 m a x r i

(4).

最后一种情况 : 当我的 i 位置的对称点回文左边界刚好与maxl重合
因为不知道当前位置外面的串的情况,因此对于这种情况暴力寻找回文串

时间复杂度估计

因为我的maxr不断向右走并且没有重复走的情况等于说我只遍历了一遍串因此时间复杂度为 O ( n )

猜你喜欢

转载自blog.csdn.net/Acer12138/article/details/81835214
今日推荐