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)。

原文地址:https://www.cnblogs.com/rookiez/p/13175192.html