【转】最长回文子串的O(n)的Manacher算法

Manacher算法

首先:大家都知道什么叫回文串吧,这个算法要解决的就是一个字符串中最长的回文子串有多长。这个算法可以在O(n)的时间复杂度内既线性时间复杂度的情况下,求出以每个字符为中心的最长回文有多长,
    这个算法有一个很巧妙的地方,它把奇数的回文串和偶数的回文串统一起来考虑了。这一点一直是在做回文串问题中时比较烦的地方。这个算法还有一个很好的地方就是充分利用了字符匹配的特殊性,避免了大量不必要的重复匹配。
    算法大致过程是这样。先在每两个相邻字符中间插入一个分隔符,当然这个分隔符要在原串中没有出现过。一般可以用‘#’分隔。这样就非常巧妙的将奇数长度回文串与偶数长度回文串统一起来考虑了(见下面的一个例子,回文串长度全为奇数了),然后用一个辅助数组P记录以每个字符为中心的最长回文串的信息。P[id]记录的是以字符str[id]为中心的最长回文串,当以str[id]为第一个字符,这个最长回文串向右延伸了P[id]个字符。
    原串:    w aa bwsw f d 
    新串:   # w # a # a # b # w # s # w # f # d #
辅助数组P:  1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1
    这里有一个很好的性质,P[id]-1就是该回文子串在原串中的长度(包括‘#’)。如果这里不是特别清楚,可以自己拿出纸来画一画,自己体会体会。当然这里可能每个人写法不尽相同,不过我想大致思路应该是一样的吧。
    好,我们继续。现在的关键问题就在于怎么在O(n)时间复杂度内求出P数组了。只要把这个P数组求出来,最长回文子串就可以直接扫一遍得出来了。
    由于这个算法是线性从前往后扫的。那么当我们准备求P[i]的时候,i以前的P[j]我们是已经得到了的。我们用mx记在i之前的回文串中,延伸至最右端的位置。同时用id这个变量记下取得这个最优mx时的id值。(注:为了防止字符比较的时候越界,我在这个加了‘#’的字符串之前还加了另一个特殊字符‘$’,故我的新串下标是从1开始的)
好,到这里,我们可以先贴一份代码了。

void pk()
{
    int i;
    int mx = 0;
    int id;
    for(i=1; i<n; i++)
    {
        if( mx > i )
            p[i] = MIN( p[2*id-i], mx-i );
        else
            p[i] = 1;
        for(; str[i+p[i]] == str[i-p[i]]; p[i]++)
            ;
        if( p[i] + i > mx )
        {
            mx = p[i] + i;
            id = i;
        }
    }
}

代码是不是很短啊,而且相当好写。很方便吧,还记得我上面说的这个算法避免了很多不必要的重复匹配吧。这是什么意思呢,其实这就是一句代码。

if( mx > i )

p[i] = MIN( p[2*id-i], mx-i );

就是当前面比较的最远长度mx>i的时候,P[i]有一个最小值。这个算法的核心思想就在这里,为什么P数组满足这样一个性质呢?

(下面的部分为图片形式)

看完这个算法,你有可能会觉得这种算法在哪会用到呢?其实回文串后缀数组也可以做。只是复杂度是O(n log n)的,而且一般情况下也不会刻意去卡一个log n的算法。可正好hdu就有这么一题,你用后缀数组写怎么都得T(当然应该是我写得太烂了)。不信的话大家也可以去试试这题。

http://acm.hdu.edu.cn/showproblem.php?pid=3068
另外,顺便附一份AC代码。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 230017

char ss[2*N+3];
int p[N],n;
char s[N];

void pk()
{
    int i;
    int mx = 0,id;
    p[0] = 2;
    for(i=1;i<n;i++)
    {
        if(mx > i)
            p[i] = min(p[2*id-i],mx-i);
        else
            p[i] = 1;
        while(ss[i+p[i]] == ss[i-p[i]])
            p[i]++;
        if(p[i] + i > mx)
        {
            mx = p[i]+i;
            id = i;
        }
    }
}

int main()
{
    int t,i;
    while(scanf("%s",s)!=EOF)
    {
        n = strlen(s);
        ss[0] = ‘@‘;
        ss[1] = ‘#‘;
        for(i=0;i<n;i++)
        {
            ss[2*i+2] = s[i];
            ss[2*i+3] = ‘#‘;
        }
        n = 2*n+2;
        ss[n] = 0;
        pk();
        int maxi = 0;
        for(i=0;i<n;i++)
            maxi = max(maxi,p[i]);
        printf("%d\n",maxi-1);
    }
    return 0;
}

【转】最长回文子串的O(n)的Manacher算法

时间: 2024-10-03 09:31:56

【转】最长回文子串的O(n)的Manacher算法的相关文章

Leetcode-最长回文子串(包含动态规划以及Manacher算法)

原文地址: https://www.cnblogs.com/mini-coconut/p/9074315.html 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 示例 1: 输入: "babad" 输出: "bab" 注意: "aba"也是一个有效答案. 示例 2: 输入: "cbbd" 输出: "bb" 自己的思路:求一个字符串的最长回文子串,我们可以将以每个字符

《算法竞赛入门经典》3.3最长回文子串

1 //例题3-4 2 /* 3 * 输入一个字符串,求出其中最长的回文子串.子串的含义是:在原串中连续出现的字符串片段. 4 *回文的含义是:正看着和倒看着相同,如abba和yyxyy.在判断时,应该忽略所有标点符号和空格 5 *且忽略大小写,但输出应保持原样(在回文串的首部和尾部不要输出多余字符).输入字符长度不超过5000 6 *且占据单独的一行.应该输出最长回文串,如果有多个,输出起始位置最靠左的. 7 *样例输入:Confuciuss say:Madam,I'm Adam. 8 *样例

Manacher算法----最长回文子串

题目描述 给定一个字符串,求它的最长回文子串的长度. 分析与解法 最容易想到的办法是枚举所有的子串,分别判断其是否为回文.这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的.同时,奇数和偶数长度还要分别考虑. Manacher算法可以解决上述问题,并在O(n)时间复杂度内求出结果.下面我们来看一下Manacher算法. 首先,为了处理奇偶的问题,在每个字符的两边都插入一个特殊的符号,这样所有的奇数或偶数长度都转换为奇数长度.比

hihocoder1302 最长回文子串

hihocoder1302 最长回文子串 先贴代码 所有的上面的提示已经交代的好清楚了…… #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <vector> #include <map> #include <queue> #include <sta

[hiho 01]最长回文子串、Manacher算法

题目描述 - 基础方法:枚举子串,判断是否为回文串. - 改进:枚举中间位置,向两侧拓展. - 再改进:利用以前的信息,使得不用每个新位置都从长度1开始拓展. - 优化:将字符串预处理为奇数长度以避免考虑条件分支. - 再优化:开头加入特殊字符避免考虑边界. Manacher 算法: id 是中心点,mx 是其边界.P[i] 表示以 i 为中心的最长回文子串的折半长度. 只要 i < mx, 以 i 为中心的回文子串就可以不必从长度1开始找,而从min{P[j], mx - i}开始(其中j为i

[C++]LeetCode: 99 Longest Palindromic Substring (最长回文子串)

题目:Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. 思路:题目要求的s的一个最长回文子串.暴力解决办法就是枚举所有的子串,再对每个子串进行回文判断.进行剪枝,我们考虑可以使用动态规划来避免重复的判

[译]最长回文子串(Longest Palindromic Substring) Part II

[译+改]最长回文子串(Longest Palindromic Substring) Part II 问题:给定字符串S,求S中的最长回文子串. 在上一篇,我们给出了4种算法,其中包括一个O(N2)时间O(1)空间的算法(中心检测法),已经很不错了.本篇将讨论一个O(N)时间O(N)空间的算法,即著名的Manacher算法,并详细说明其时间复杂度为何是O(N). 提示 +BIT祝威+悄悄在此留下版了个权的信息说: 先想想有什么办法能改进中心检测法. 考虑一下最坏的情况.★ 最坏的情况就是各个回文

URAL 1297 后缀数组:求最长回文子串

思路:这题下午搞了然后一直WA,后面就看了Discuss,里面有个数组:ABCDEFDCBA,这个我输出ABCD,所以错了. 然后才知道自己写的后缀数组对这个回文子串有bug,然后就不知道怎么改了. 然后看题解,里面都是用RMQ先预处理任意两个后缀的最长公共前缀,因为不太知道这个,所以又看了一下午,嘛嘛-- 然后理解RMQ和后缀一起用的时候才发现其实这里不用RMQ也可以,只要特殊处理一下上面这个没过的例子就行了,哈哈--机智-- 不过那个国家集训队论文里面正解是用RMQ做的,自己还得会和RMQ一

最长回文子串(转自:hihoCoder)

#1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:"小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?" 小Ho奇怪的问道:"什么叫做最长回文子串呢?" 小Hi回答道:"一个字符串中连续的一