manacher(马拉车)算法

断断续续地看了两天的马拉车算法,可算是给搞明白了(贼开心),这算是自己搞懂的第一个算法了(23333333333333)这个算法照目前自己的理解来看,貌似就只能求个字符串中的回文串(接触这个算法是要求最长的回文串),虽然应用的范围有点少,但还是要学习滴,不然遇到类似的题目就gg了。

可以在线性时间内求得答案,时间复杂度为O(n)。

1、回文串的个数是可奇可偶的,碰上奇数的回文串还可以,如果是偶数的回文串没有着脚点,那就很恼人了。所以马拉车算法会对字符串进行预先处理,然后再求最长的回文串。首先用字符串中没有出现过的字符来表示串中每个元素的间隔,而为了防止在访问时出现越界情况,需要在串首和串尾再加上额外的特殊字符。

例如:原串为ababab;处理完之后就是$#a#b#a#b#a#b#$;  其实对于最后一个$,也可以不加,因为字符串的最后一个字符是‘\0’就相当于一个特殊字符了。

    //设t为将要进行预处理的字符串,则处理实现如下
    string t="@#";
    for(int i=0;i<str.size();i++)
    {
        t=t+str[i];
        t=t+"#";
    }

2、接下来就是在新串中找以每一个字符为中心的回文串就可以了。manacher算法的思想就是从左到右求出以每个字符为中心的最长回文串。设能延伸到最右边的字符串的中心位置为id,该字符串最右端的位置为mx,pal数组来储存此处回文串的长度。因为回文串有对称的性质,所以后边的字符串可以通过对称来直接求得其长度(当然前边的还是需要乖乖的遍历求出来的)。

3、对于遍历到的字符(下标设为i),一共会有三种情况;

 (1)i<=mx;

        情况如下图所示(第一次用画图的我表示已经被逼疯了emmmmm)

  

        该情况下就万事大吉了,直接把2*id-i处串的长度,复制给i就OK了。

  (2)i<=mx

        同样是这种情况但是出现i处的回文串超出了mx的情况如下图

        最右端超出了mx的范围,出现什么情况就不好说了,所以只能暴力一下,然后更新mx的大小就可以了

    (3)i>mx

        这种情况直接暴力,求此处回文串的长度即可。附上自己写的代码

    int mx=0,id=0,len=0;
    vector<int> pal(t.size(),0);
    for(int i=1;i<t.size();i++)
    {
        //pal数组存的其实是此处回文串的长度+1
        pal[i]=mx>i ? min(pal[2*id-i],mx-i):1;//此处为最关键的一处
        //当 mx>i 当然是要选一个小的来赋值,这样就可以防止超出mx了
        //然后再接着暴力枚举后边的是不是回文串,以使它的长度增加。
        //当 mx<=i时,此处的长度为1,直接进入暴力枚举它的长度
        while(t[i+pal[i]]==t[i-pal[i]] && ss.count(t[i+pal[i]]))
        {
            ++pal[i];
        }
        //最右端回文串的长度超出mx后就要进行mx和id的更新了
        if(mx < i+pal[i])
        {
            mx = i+pal[i];
            id=i;
        }
        if(len < pal[i]-1)
        {
            len=pal[i]-1;
        }
    }

        OK以上就是自己总结的马拉车算法了。


猜你喜欢

转载自blog.csdn.net/sykai1/article/details/79905291