求最长回文子串(Longest Palindromic Substring)
2013-11-22 11:12
387 查看
求解最长回文子串的问题最近经常遇到,特别是近期的笔试,因而有了一个学习的契机。先说说回文字符串,回文字符串的意思是从左往右看和从右往左看都是一样的,即我们如果以中心为轴,这个字符串是左右对称的,如字符串"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)的时间复杂度。
动态规划法
假设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)的空间。
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),且不需要使用额外的空间。
代码如下,欢迎指导交流~
除了以上介绍的2种方法外,还有一种Manacher算法的O(n)的算法,待有时间学习时再做更新。
用暴力的方法是不现实的,一个字符串的子串个数很多(用2个for循环求解,i : 0-n-1,j : i- n-1),有n * (n+1) / 2个,如果对所有子串都进行回文判断,需要O(n3),显然耗时巨大。
常规的求解方法有2种:动态规划法,中心扩展判断法,这2种算法都是O(n2)的时间复杂度。
动态规划法
假设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)的空间。
//Written by zhou //2013.11.22 string longestPalindrome(string s) { size_t n = s.length(); bool **dp = new bool* ; for (size_t i = 0; i < n; ++i) { dp[i] = new bool ; } //为基准情况赋值 int startPos = 0; int max = 1; for (size_t i = 0; i < n; ++i) { dp[i][i] = true; if (i + 1 < n) { if (s[i] == s[i+1]) { dp[i][i+1] = true; startPos = i; max = 2; } else dp[i][i+1] = false; } } //动规求解,前面已求len = 1, len = 2的情况 for (int len = 3; len <= n; ++len) { for (int i = 0; i < n - len + 1; ++i) { int j = i + len - 1; if (dp[i+1][j-1] && s[i] == s[j]) { dp[i][j] = true; int curLen = j - i + 1; if (curLen > max) { startPos = i; max = curLen; } } else dp[i][j] = false; } } //释放二维数组 for (size_t i = 0; i < n; ++i) delete[] dp[i]; delete[] dp; return s.substr(startPos,max); }
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),且不需要使用额外的空间。
代码如下,欢迎指导交流~
//Written by zhou //2013.11.22 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; }
除了以上介绍的2种方法外,还有一种Manacher算法的O(n)的算法,待有时间学习时再做更新。
相关文章推荐
- [Leetcode] Longest palindromic substring 最长回文子串
- LeetCode Longest Palindromic Substring(最长回文子串)
- Longest Palindromic Substring 最长回文子串
- java常用算法之最长回文子串(Longest Palindromic Substring)
- leetcode (5) - Longest Palindromic Substring 最长回文子串
- LeetCode_5---Longest Palindromic Substring (求最长回文子串)
- LeetCodeOJ_5_m_Longest Palindromic Substring(最长回文子串)
- leetcode Longest Palindromic Substring最长回文子串
- Longest Palindromic Substring(最长回文子串)
- 浅谈manacher算法 最长回文子串(Longest Palindromic Substring)
- 最长回文子串(Longest Palindromic Substring)
- 最长回文子串(Longest Palindromic Substring)-DP问题
- [译+改]最长回文子串(Longest Palindromic Substring) Part II
- manacher算法求最长回文子串(Longest Palindromic Substring)
- **[Lintcode]Longest Palindromic Substring最长回文子串
- LeetCode-5 Longest Palindromic Substring(求最长回文子串)
- LeetCode题库解答与分析——#5.最长回文子串LongestPalindromicSubstring
- LeetCode 5 Longest Palindromic Substring(最长回文子串,暴力剪枝/DP/曼彻斯特算法)
- 【翻译】Longest Palindromic Substring 最长回文子串
- LeetCode---5. Longest Palindromic Substring(最长回文子串)