【LeetCode】5.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.


翻译:

           给定一个字符串,找出它的最大回文子串,返回。


分析:

          所谓”回文“,是指倒着读和正着读结果都一样的字符串,例如”abcba"或“abba"。

         这个题目有很多种解法,下面将依次进行分析。


        1.Brute force (暴力破解法)

         即依次得到给定字符串的每一个子串,判断这个子串是否为回文串,找到最长回文子串。

         暴力破解法获取每个子串用了两个for循环(O(n^2)),在判断该子串是否为回文时又用了一个while循环(O(n)),所以时间复杂度为O(n^3)。

        代码如下,这段代码在OJ上是没法通过的,因为当字符串很长时会超时。

class Solution {
public:
    string longestPalindrome(string s) {
         int startLP=0;//记录最长回文子串的起始位置
        int maxLen=0;//记录最长回文子串的长度
        for(int i=0;i<s.length();i++)
            for(int j=i+1;j<s.length();j++)
            {
                int start=i,end=j;
                while(start<=end&&s[start]==s[end])//判断该子串是否为回文
                {
                    start++;
                    end--;
                }
                if(start>end)//若该子串为回文子串
                {
                    if((j-i+1)>maxLen)
                    {
                        startLP=i;
                        maxLen=j-i+1;
                    }
                }
            }
        return s.substr(startLP,maxLen);
    }
};

       2.Dynamic Programming(动态规划)

      动态规划是暴力法的一个进化版本,我们没必要每一次都重新判断一个子串是否为回文串,可以将一些辅助判断的信息记录下来供后来者使用,这样后面的回文串判断就仅需O(1)的时间,而不是暴力法中的O(n)。若字符串从i..j的子串是回文,则字符串总(i+1)...(j-1)的子串也是回文,由此最长回文子串就可以分解成一序列子问题。

      定义P[i, j]=1表示子串s[i,..., j]是回文串,P[i, j]=0表示子串s[i,..., j]是不是回文串,则得到状态方程和转移方程为:

      

       因此,使用动态规划的方法来求解需要额外O(n^2)空间,时间复杂度也为O(n^2)

       在具体实现中,由于判断长度为n的子串是否为回文串需要借助长度为(n-2)的子串是否为回文串来判断,因此自底向上进行计算,初始化长度为1和长度为2的子串的P[i, j],然后依次计算长度为3,4,... ,s.length()的子串的P[i, j],计算过程同时更新最长回文子串的起始位置和长度。代码如下:

 

class Solution {
public:
    string longestPalindrome(string s) {
        int P[s.length()][s.length()]={0};
        int startLP=0;//记录最长回文子串起始位置
        int maxLen=0;//记录最长回文子串长度
        for(int i=0;i<s.length();i++)
        {
            P[i][i]=1;
            startLP=i;
            maxLen=1;
        }
        for(int i=0;i<s.length()-1;i++)
        {
            if(s[i]==s[i+1])
            {
                P[i][i+1]=1;
                startLP=i;
                maxLen=2;
            }
        }
        for(int len=3;len<=s.length();len++)
        {
            for(int i=0;i<s.length()-len+1;i++)
            {
                int j=i+len-1;
                if(s[i]==s[j]&&P[i+1][j-1]==1)
                {
                    P[i][j]=1;
                    startLP=i;
                    maxLen=len;
                }
            }
        }
        return s.substr(startLP,maxLen);
    }
};
      (这里有一个小疑惑,一样的思路,我用C#写的时候,当字符串过长时会发生Memory Limit Exceeded,但是重新用C++写了之后,就不出现这样的问题了,这是为什么?好吧,原谅我这放荡不羁的编程语言,一会儿用这,一会儿用那~)


      3. 中心扩展法       

          中心扩展法的基本思想是依次以每一个字符为中心,向两边扩展,查找以这个字符未中心的最长回文串,从而找到最终的最长回文串。需要注意的是回文串长度为单数和双的情况,例如:“abcba"和”abba",因此在向两边扩展时需要考虑以该字符为中心的扩展,以及以该字符及其下一个字符为中心的扩展,这样才能包含回文串长度为双数的情况。

       中心扩展法需要遍历一遍字符串对每一个字符进行扩展,扩展的时间复杂度为O(n),由此得到中心扩展法的时间复杂度为O(n^2),空间复杂度为O(1)。代码如下:

class Solution {
public:
    string expansion(string s,int start1,int start2)
    {
        int l=start1,r=start2;
        while(l>=0&&r<s.length()&&s[l]==s[r])
        {
            l--;
            r++;
        }
        return s.substr(l+1,r-1-l);
    }
    
    string longestPalindrome(string s) {
         if(s.length()==1)
            return s;
         string lp="";
         for(int i=0;i<s.length()-1;i++)
         {
            string tempStr=expansion(s,i,i);
            if(tempStr.length()>lp.length())
                lp=tempStr;
            tempStr=expansion(s,i,i+1);
             if(tempStr.length()>lp.length())
                lp=tempStr;
         }
         return lp;
    }  
};
 

      4.Manacher算法

      ......明天继续


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章