最长回文子串

hihocoder 的一道题目,说是很经典的题目,但是自己还没做过,看来非ACMer差距还是巨大的。。
然后自己搞了半天也只搞出了O(n^2)的算法,看了一下线性算法,太复杂了,本吊估计是想不出了。

附上O(n^2)的基于移动中心的算法

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <string.h>
#include <string>
#include <algorithm>
#include <sstream>
#include <iomanip>
#define Min(a,b) (((a) < (b)) ? (a) : (b))
#define Max(a,b) (((a) > (b)) ? (a) : (b))
#define read freopen("in.txt","r",stdin)  
#define write freopen("out.txt","w",stdout)
using namespace std;
int maxdrome(string str)//O(n^3)
{
    int maxlen=1;
    for(int length=2;length<=str.size();length++)//length
    {
        for(int i=0;i<=str.size()-2;i++)//start index
        {
            //for(int j=i+1;j<=n-1;j++)
            //{
            if(i+length-1>=str.size()) continue;
            int k;
            for(k=0;k<length/2;k++)
            {
                if(str[i+ k]==str[i+length-1 - k])
                    ;
                else
                    break;
            }
            if(k==length/2)
            {
                if(maxlen<length)
                    maxlen=length;
            }
            //}
        }
    }
    return maxlen;
}
int maxdrome_centre(string str)
{
    if(str.size()==0) return 0;
    int maxlen=1,max;
    for(int i=1;i<str.size()-1;i++)
    {
        max=1;
        for(int j=1;j<=(str.size()-1)/2;j++)//two
        {
            if(i-j>=0 && i+j <=str.size()-1)
            {
                if(str[i-j] == str[i+j])
                    max+=2;
                else
                    break;
            }
            else
                break;
        }
        if(max>maxlen)
            maxlen=max;
        max=1;
        if(str[i]==str[i+1])
        {
            max++;
            for(int j=1;j<=(str.size()-2)/2;j++)
            {
                if(i-j>=0 && i+1+j<=str.size()-1 )
                {
                    if(str[i-j]==str[i+1+j])
                        max+=2;
                    else
                        break;
                }
                else
                    break;
            }
            if(max>maxlen)
                maxlen=max;
        }


    }
    return maxlen;
}
int main()
{
    //read;
    //write;
    int n;
    //cin.clear();
    //cin>>n;
    //cin.clear();
    scanf("%d",&n);
    string str;
    //cin>>str;
    getline(cin,str);
    while(n>0)
    {
        getline(cin,str);
        //if(str.size()==0)
        //    break;
        cout<<maxdrome_centre(str)<<endl;
        n--;
    }

    return 0;
}

我当时还请教了高富帅,发现他好像也没有想出线性算法,关键是里面有个推导出了一个不等式性质,
P[i] >= MIN(P[2 * id - i], mx - i)

线性算法是基于这个不等式的,数学不够屌是搞不出来的,或者之前做过,我现在也没完全理清楚这个头绪,咳。。。

之前写了一个O(n^3) O(n^2)的算法,感觉很像是最长字段和从n^3 一直杀到线性,正如Lucida所说大为所快,但是最后线性的其实
不算是DP,我也没搞出来。。

http://blog.163.com/zhaohai_1988/blog/static/2095100852012716105847112/

今天看到邹博的PPT,决定这道题目 一定要过掉,包括leetcode上,还有东北大学大一ACMer翟神居然也会,我必须过了。现在完全理解了这个不等式了,P[i]>=Min(P[2id-i],mx-i)
其实本质应该这么理解,如何由P[0..i-1] 推导出P[i], 因为设定三元组 [i,P[i],i+P[i]], mx=k max{k+P[k]}, 0<=k<=i-1 也即最远到的位置的后一个,所以是mx) 开区间,当mx>i的时候,说明可以借助P[0…i-1]里面的最大mx的
那个的mx信息控制i,mx>i 那么基于两次对称性,id(0..i-1最大)对称,以及j(=2
id-i)对称, 当mx-i>P[2*id-i=j], P[i]=P[j], 否则,P[i]>=mx-i, 具体等于多少,从延伸出的P[i]两端继续延伸,可能由于计算过操作次数
任然是线性的。

另外记得把原字符串扩展井号,但是如果字符串有井号咋办呢。。。

class Solution {
    public:
            string longestPalindrome(string s) 
            {
                s=addjing(s);
                int n=s.size();
                int *P=new int[n];
                int id=0,mx=1;
                P[0]=1;
                for(int i=1;i<n;i++)
                {
                    if(mx>i)
                    {
                        if(mx-i>P[2*id-i])
                            P[i]=P[2*id-i];
                        else
                            P[i]=mx-i;
                    }
                    else
                        P[i]=1;
                    for(;s[i-P[i]]==s[i+P[i]];P[i]++);
                    if(mx<i+P[i])
                    {
                        mx=P[i]+i;
                        id=i;
                    }
                }
                int maxlen=1,maxi=0;
                for(int i=0;i<n;i++)
                {
                    if(maxlen<P[i])
                    {
                        maxi=i;
                        maxlen=P[i];
                    }
                }
                delete[] P;
                string result=s.substr(maxi-maxlen+1,maxlen*2-1);
                string newresult="";
                for(int i=0;i<(int)result.size();i++)
                {
                    if(result[i]!='#')
                        newresult+=result[i];
                }
                return newresult;
            }
            string addjing(string s)
            {
                string adds="#";
                for(int i=0;i<(int)s.size();i++)
                {
                    adds+=s[i];
                    adds+='#';
                }
                return adds;
            }
};

Posted by richard爱闹 - 7月 9 2014
如需转载,请注明: 本文来自 Richard