• 最长回文字符串_Manacher算法_(O(n))


         朴素算法求最长回文字符串包括奇数长的和偶数长的,求的时候都要分情况讨论,Manacher算法做了一个简单的处理,很巧妙地把奇数长度回文串与偶数长度回文串统一考虑,也就是在每个相邻的字符之间插入一个分隔符,串的首尾也要加,当然这个分隔符不能再原串中出现,一般可以用‘#’或者‘$’等字符。例如:
    原串:abaabb
    新串:#a#b#a#a#b#b#
    这样一来,原来的奇数长度回文串还是奇数长度,偶数长度的也变成以‘#’为中心奇数回文串了。(读者可以自己写着试试,自己检验)

    下面我们来讨论算法。

    算法思路:把原串每个字符中间用一个串中没出现过的字符分隔开来(统一奇偶),用一个数组p[ i ]记录以 s[i]为中间字符的回文串向右能匹配的长度。先看个例子:(为了防止数组越界我在开头加了一个$,实际上新串就从下标1开始)(参考博客:http://blog.csdn.net/ywhorizen/article/details/6629268)

    原串:       a  b  a  a  b  b

    新串: $    #   a   #   b   #    a   #   a   #   b  #    b   #

                0   1   2   3   4    5   6   7   8   9  10  11 12  13

    p数组:     1   2   1   4   1    2   5   2   1   2   3    2   1

    可以看出p[i]-1就是回文串的长度,下面予以证明:

    假设以i为中心的回文串长度为S,因为p[ i ]记录以 s[i]为中间字符的回文串向右能匹配的长度,所以有

    S=2*p[ i ]-1;

         又因为此时串中加了其他字符#,以s[i]为中心的回文串一定是以#开头和结尾的,以#为中间字符的就是长度为偶数的,以非#号为中间字符的就是长度为奇数的,例如“#b#b#”或“#b#a#b#”所以L 减去最前或者最后的‘#’字符就是原串中长度的2倍,即原串长度为(S-1)/2,化简的P[i]-1。

          接下来就是求p数组了:

            为了防止求P[i]向两边扩展时可能数组越界,我们需要在数组最前面和最后面加一个特殊字符,令P[0]=‘$’最后位置默认为‘’不需要特殊处理。此外,我们用mx 变量记录在求i 之前的回文串中,延伸至最右端的位置,同时用id 记录取这个mx 的id 值。通过下面这句话,算法避免了很多没必要的重复匹配。

    if (mx > i){
                 p[i] = min(p[2*id - i], maxid - i);
             }

           从左到右计算p[i]时 p[0.....i-1] 都以计算出,并且用一个变量mx记录 max{ k+p[ k ] } (k=0.....i-1),用id记录取最大值时的k, 则 p[ i ]= min( p[2*id - i ], mx - i )(这里需要好好思考,最好自己拿笔画画)

    aa

          对于第一幅图以i为中间字符的回文串被以id为中间字符的回文串所覆盖,由对称性,p[ i ] = p[ 2*id - i ] 。对于第二幅图没有完全被覆盖,所以对于k>mx的字符,要一个一个匹配,才能确定p [ i ]。

        下面给出代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=2*1000005;
    char a[maxn],b[maxn];
    int p[maxn];
    int main()
    {
        int t,m;
        scanf("%d",&t);
        while(t--)
        {
            m=0;
            int ans=0;
            int mx=0,id=0;
            scanf("%s",a);
            int n=strlen(a);
            b[0]='$';
            b[1]='#';
            for(int i=0;i<n;i++)
            {
                b[2*i+2]=a[i];
                b[2*i+3]='#';
            }
            b[2*n+2]='';
            int m=strlen(b);
            for(int i=1;i<m;i++)
            {
                if(mx>i)
                    p[i]=min(p[2*id-i],mx-i);
                else
                    p[i]=1;
                while(b[i+p[i]]==b[i-p[i]])
                {
                    p[i]++;
                }
                if(p[i]+i>mx)
                {
                    mx=p[i]+i;
                    id=i;
                }
                if(ans<p[i])
                    ans=p[i];
            }
            printf("%d
    ",ans-1);
        }
    }
    





  • 相关阅读:
    网络协议(一)HTTP请求报文(请求行、请求头、请求体),响应报文
    Java集合(五) Set 添加元素
    Java 泛型面试题
    并发编程-线程池(二)线程池回收线程
    Java(二)equal 和 hashcode使用
    Java 泛型(一)作用和使用
    Java排序(一)实现类的排序
    Java排序(二)排序算法的稳定性
    SDL 程序编译错误-undefined reference to `SDL_GetError'
    SDL程序编译错误-error: conflicting types for ‘_XData32’
  • 原文地址:https://www.cnblogs.com/Zeroinger/p/5493908.html
Copyright © 2020-2023  润新知