最长回文子串-manacher算法

像kmp一样,先来看一道题目
给出一个长度为n的字符串S,
求S的子串T,令T反转后T`与T完全相等,求T最大的长度。
首先,可以想到用暴力做,枚举所有的子串,然后判断,时间复杂度为 O(N3)
第二,可以发现,如果子串S[1..5]是一个回文串,那么S[2..4]自然也是一个回文串。
利用这个性质,可以枚举所有串的回文中心,然后从中间向两边去查找、判断。
最后的时间复杂度为 O(N2)
显然,这仍然不够优,kmp能够实现线性时间完成,那么这个问题也能用线性的时间完成吗?
答案是肯定了,这就需要manacher这个算法来实现。
首先,可以充分利用刚才发现的规律,就可以通过左半部分的对称中心的答案得到右半边的对称中心的答案
如果是一个回文串S
S:caaabaaaaabaaac
那么,可以得到一个数组 ans
分别是 1131713F31?
这个F就是十六进制下的15
可以发现,F所在的位置就是S串的中心,
首先,以此可以发现 S[1..15]=S[1..15]
然后根据 S[5] 的数字发现 S[2..8]=S[2..8]
再根据上一句,可以得到 S[2..8]=S[8..14]
那么 S[11] 至少为7
最后比较发现 S[7]S[15]
所以 S[11] 的答案小于9,最后得到7
所以manacher算法的原理就是从左到右求各个中心的答案,然后根据最长的回文串再来根据左边的回文中心的答案推出右边的答案。
如果需要 S[i] 的答案,就查询 S[2pi]
用这种方法可以避免重复比较,也充分地利用了前面的信息
但是现在仍然有一个问题,如果长度是偶数的怎么解决。
答案是在字符串前加上一个未出现过的字符,然后在原有的字符之间再插入这个字符,最后再加上一个,但是防止越界,于是在开头再放一个另一种字符,在结尾也放一个没有出现过的字符。
这样做,就可以把所有偶数的情况都转化为奇数。
以下为代码:

int manacher() {
    int mx = 0, ans = 0, po = 0;

    for(int i = 1; i <= lenT; i++) {
        if(mx > i) {
            Len[i] = min(mx - i, Len[2 * po - i]);
        } else {
            Len[i] = 1;
        }
        while(T[i - Len[i]] == T[i + Len[i]]) {
            ++Len[i];
        }


        if(Len[i] + i > mx) {
            mx = Len[i] + i;
            po = i;
        }

        ans = max(ans, Len[i]);
    }

    return ans - 1;
}

那么,最后再来看一下时间复杂度,不难猜到,这也是利用线性时间就能解决问题的算法,这个算法在运行时,while每运行一次,p至少向右移一下,所以while最多执行N次,其他的跟着for循环最多执行N次,所以最终总时间为 O(N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值