Longest Palindromic Substring

首先表示感谢博主,http://blog.csdn.net/zhouworld16/article/details/16842467

  求解最长回文子串的问题最近经常遇到,特别是近期的笔试,因而有了一个学习的契机。先说说回文字符串,回文字符串的意思是从左往右看和从右往左看都是一样的,即我们如果以中心为轴,这个字符串是左右对称的,如字符串"abcba","abba"。字符串"abcba"有奇数个字符,所以以中间字符'c'为轴左右对称,而字符串"abba"有偶数个字符,所以是对半开来对称的。而顾名思义,最长回文子串就是指一个字符串中最长的具有回文性质的子串了。

    用暴力的方法是不现实的,一个字符串的子串个数很多(用2个for循环求解,i : 0-n-1,j : i- n-1),有n * (n+1) / 2个,如果对所有子串都进行回文判断,需要O(n3),显然耗时巨大。

    常规的求解方法有2种:动态规划法,中心扩展判断法,这2种算法都是O(n2)的时间复杂度。

  1. 动态规划法

      假设dp[ i ][ j ]的值为true,表示字符串s中下标从 i 到 j 的字符组成的子串是回文串。那么可以推出:

    dp[ i ][ j ] = dp[ i + 1][ j - 1] && s[ i ] == s[ j ]。

    这是一般的情况,由于需要依靠i+1, j -1,所以有可能 i + 1 = j -1, i +1 = (j - 1) -1,因此需要求出基准情况才能套用以上的公式:

    a. i + 1 = j -1,即回文长度为1时,dp[ i ][ i ] = true;

    b. i +1 = (j - 1) -1,即回文长度为2时,dp[ i ][ i + 1] = (s[ i ] == s[ i + 1])。

    有了以上分析就可以写出代码了。需要注意的是动态规划需要额外的O(n2)的空间。

   

  1. string longestPalindrome(string s) {  

  2.   

  3.         size_t n = s.length();  

  4.         bool **dp = new bool*[n];  

  5.         for (size_t i = 0; i < n; ++i)  

  6.         {  

  7.             dp[i] = new bool[n];  

  8.         }  

  9.                   

  10.         //为基准情况赋值  

  11.         int startPos = 0;  

  12.         int max = 1;  

  13.         for (size_t i = 0; i < n; ++i)  

  14.         {  

  15.             dp[i][i] = true;  

  16.             if (i + 1 < n)  

  17.             {  

  18.                 if (s[i] == s[i+1])  

  19.                 {  

  20.                     dp[i][i+1] = true;  

  21.                     startPos = i;  

  22.                     max = 2;  

  23.                 }  

  24.                 else dp[i][i+1] = false;  

  25.             }  

  26.         }  

  27.           

  28.         //动规求解,前面已求len = 1, len = 2的情况  

  29.         for (int len = 3; len <= n; ++len)  

  30.         {  

  31.             for (int i = 0; i < n - len + 1; ++i)  

  32.             {  

  33.                 int j = i + len - 1;  

  34.                   

  35.                 if (dp[i+1][j-1] && s[i] == s[j])  

  36.                 {  

  37.                     dp[i][j] = true;  

  38.                     int curLen = j - i + 1;  

  39.                     if (curLen > max)  

  40.                     {  

  41.                         startPos = i;  

  42.                         max = curLen;  

  43.                     }  

  44.                 }  

  45.                 else dp[i][j] = false;  

  46.                   

  47.             }  

  48.         }  

  49.           

  50.         //释放二维数组  

  51.         for (size_t i = 0; i < n; ++i)  

  52.            delete[] dp[i];  

  53.           

  54.         delete[] dp;          

  55.         return s.substr(startPos,max);  

  56.     }  

     

      2. 中心扩展法

      因为回文字符串是以中心轴对称的,所以如果我们从下标 i 出发,用2个指针向 i 的两边扩展判断是否相等,那么只需要对0到

n-1的下标都做此操作,就可以求出最长的回文子串。但需要注意的是,回文字符串有奇偶对称之分,即"abcba"与"abba"2种类型,

因此需要在代码编写时都做判断。

     设函数int Palindromic ( string &s, int i ,int j) 是求由下标 i 和 j 向两边扩展的回文串的长度,那么对0至n-1的下标,调用2次此函数:

     int lenOdd =  Palindromic( str, i, i ) 和 int lenEven = Palindromic (str , i , j ),即可求得以i 下标为奇回文和偶回文的子串长度。

     接下来以lenOdd和lenEven中的最大值与当前最大值max比较即可。

     这个方法有一个好处是时间复杂度为O(n2),且不需要使用额外的空间。

     代码如下,欢迎指导交流~

    


string longestPalindrome(string s) {

       

        size_t n = s.length();

        int startPos = 0;

        int max = 1;

        for (int i = 0; i < n; ++i)

        {

            int oddLen = 0, evenLen = 0, curLen;

            oddLen = Palindromic(s,i,i);

            

            if (i + 1 < n)

               evenLen = Palindromic(s,i,i+1);

            

            curLen = oddLen > evenLen? oddLen : evenLen;

            

            if (curLen > max)

            {

                max = curLen;

                if (max & 0x1)

                  startPos = i - max / 2;

                else 

                  startPos = i - (max - 1) / 2;

            }

        }

        

        return s.substr(startPos,max);

    }

    

    int Palindromic(const string &str, int i, int j)

    {

        size_t n = str.length();

        int curLen = 0;


        while (i >= 0 && j < n && str[i] == str[j])

        {

--i;

            ++j;

        }

        curLen = (j-1) - (i+1) + 1;


        return curLen;

    }




你可能感兴趣的:(字符串,动态,中心,暴力,左右)