LeetCode5:最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
这题有4种解法,暴力解法,中心扩散法,动态规划,manacher算法。暴力算法不做解释,manacher太高深,先不学了。
中心扩散法
从边界情况,即子串长度为1或者2的情况向两边扩散,如果两边相同就可以加长回串的长度,最后返回最长的子串。
1 class Solution { 2 public: 3 pair<int, int> expandAroundCenter(const string& s, int left, int right) { 4 while (left >= 0 && right < s.size() && s[left] == s[right]) { 5 --left; 6 ++right; 7 } 8 return {left + 1, right - 1}; 9 } 10 11 string longestPalindrome(string s) { 12 int start = 0, end = 0; 13 for (int i = 0; i < s.size(); ++i) { 14 auto [left1, right1] = expandAroundCenter(s, i, i); 15 auto [left2, right2] = expandAroundCenter(s, i, i + 1); 16 if (right1 - left1 > end - start) { 17 start = left1; 18 end = right1; 19 } 20 if (right2 - left2 > end - start) { 21 start = left2; 22 end = right2; 23 } 24 } 25 return s.substr(start, end - start + 1); 26 } 27 }; 28 29 作者:LeetCode-Solution 30 链接:https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/ 31 来源:力扣(LeetCode) 32 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
长度为1的中心子串有n个,长度为2的中心子串有n-1个,每个中心子串扩展都是O(n)次,时间复杂度为O(n^2),空间复杂度为O(1)。
动态规划
动态规划在于找到状态转移方程。如果一个子串是回文串,而且该串的两侧的两个字符是相同的,那么加上两次的字符的新串也是回文串。
转移方程可以写成如下形式:
而对于边界条件,要么中心子串是一个字符,要么是两个相同的字符。
1 class Solution { 2 public: 3 string longestPalindrome(string s) 4 { 5 if(s.size()==0) return s; 6 int n = s.size(); 7 vector<vector<int>> dp(n, vector<int>(n)); 8 int maxlen = 0, maxi = 0, maxj = 0; 9 10 dp[0][0]=1; 11 12 for (int j = 1; j < n; j++) 13 { 14 dp[j][j]=1; 15 dp[j-1][j]=(s[j-1]==s[j])?2:0; 16 if(dp[j-1][j]>maxlen){ 17 maxlen = dp[j-1][j]; 18 maxi = j-1; 19 maxj = j; 20 } 21 for (int i = 0; i < j-1; i++) 22 { 23 if ((s[i] == s[j]) && ((dp[i + 1][j - 1] != 0) || (j - i < 3))) 24 { 25 dp[i][j] = dp[i + 1][j - 1] + 2; 26 if (dp[i][j] > maxlen) 27 { 28 maxlen = dp[i][j]; 29 maxi = i; 30 maxj = j; 31 } 32 } 33 else 34 dp[i][j] = 0; 35 } 36 } 37 return s.substr(maxi, maxj-maxi+1 ); 38 } 39 };
这里采用给一个上三角的n*n的二位数组记录这个位置横纵坐标(i,j)对应的子串(即从i到j)是否为回文串,如果不是则为0,如果是则代表该回文子串的长度。
因为(i,j)是否为回文取决于(i+1,j-1),所以需要先计算出每一个位置左下方的值,才能得出当前位置的值。边界条件即为[i][i]位置全为1,[i-1][i]位置的值取决于这两个位置的字符是否相同。
循环按照按列循环的顺序进行,一共进行n-1次列循环,对于每列进行n-2次行循环,时间复杂度为O(n^2),空间复杂度为O(n^2)。