Manaher算法总结

对于Manacher算法,主要的作用是用来求一个字符串的最长回文子串。
这个算法的时间复杂度书线性的,即O(n)
下面我分两个部分来讲

1)预处理

这个算法的精妙之处在于巧妙地避免了考虑回文子串的长度是奇数还是偶数(如果你还不知道什么是回文数,回文串,请自行baidu)
在Manacher算法中,需要提前预处理我们原本的字符串,这里把原串叫做s1, 把预处理之后的字符串叫做s2.
那么,对于s1 = "ababba",
预处理后的s2 = "$#a#b#a#b#b#a#".
这样一来,所有的回文串,在s2中,都是奇数了,你可以自己画一画或者看一看。这里注意一点,我们将s2[0] = '$',也就是一个字符串中没有出现的其他字符,为的是避免访问数组时越界。如果没有这个处理。在代码中的P[i] = min(P[2*id]-i),mx-i)处可能会出现这种情况:
当进行到第二次循环,此时i=1,id = 0,2*id-i = -1,就出现了数组越界。。所以这个处理是必须的。

void init()
{
    int i, j = 2;
    s2[0] = '$', s2[1] = '#';
    
    for(i=0;s1[i];i++)
    {
        s2[j++] = s1[i];
        s2[j++] = '#';
    }
    s2[j] = '\0';
}


2)Manacher算法

预处理完成之后,就可以用Manacher算法来求最长回文子串了。
你会发现,所有和字符串相关的算法都很注重利用之前匹配过程中留下的有用信息,当然这个算法也不能例外。

首先,这里有一个辅助数组P[](针对s2而言的),记录的是在P[i]处,包括P[i]本身向左向右延伸的最大字符数。仔细想想你会明白P[i]-1表示的正是原串中对应位置的最回文子串的长度。  

//引用前面文章里面的内容
下面以字符串12212321为例
经过上一步,变成了 S[] = "$#1#2#2#1#2#3#2#1#";
然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i],也就是把该回文串“对折”以后的长度),比如S和P的对应关系:
---------------------------------------------------------
S   #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #
P   1  2  1  2  5  2  1  4  1  2  1  6  1  2  1  2  1
---------------------------------------------------------
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)

再来看怎么匹配以及利用有用信息,这里我们用一个变量 id 记录扩张长度位置信息, 用变量 mx 记录id位置处的扩张长度。
先给出代码,结合代码来讲
void manacher(char* s)
{
    int i, id = 0, mx = 0;
    P[0] = 0;              //P[0]位置没用
    for(i=1;s[i];i++)      //对串进行线性扫描
    {
        if(mx > i)         //如果mx比当前i大,分为两种情况,详细致请看文章开头推荐的blog上的图示,非常给力的图
            P[i] = min(P[2*id-i],mx-i);
        else               //如果mx比i小,没有可以利用的信息,那么就只能从头开始匹配
            P[i] = 1;
        while(s[i + P[i]] == s[i - P[i]])    P[i]++;  //匹配
        if(mx < P[i] + i) //坚持是否有更新mx以及id
        {
            mx = P[i] + i;
            id = i;
        }
    }
}

以上内容参考:http://www.cnblogs.com/BigBallon/p/3816890.html


最后做些练习上上手:


hdu3068    poj3796      hdu3294        hdu4513      hdu5371(多校7_1003题)


点题号为解题报告;寻找&星空の孩子  



你可能感兴趣的:(算法,manacher算法,字符串回文)