最长回文子串-manacher算法

来源:互联网 发布:网络诈骗有哪些 编辑:程序博客网 时间:2024/06/10 02:14

像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)

0 0
原创粉丝点击