一、题目
-
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
-
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。 -
示例 2:
输入: “cbbd”
输出: “bb” -
链接: 5.最长回文串
二、分析
-
暴力匹配
- 最简单也是最直接的方法。直接暴力遍历字符串,枚举所有长度>= 2的子串,依次判是否是回文。具体实现时,记录当前回文串的长度,可以根据当前的最大长度 进行 回文验证。在记录最长回文子串的时候,可以只记录”当前子串的起始长度“ 和”子串长度“。 最后截取字符串,返回结果
- 时间复杂度:O(n^3) 两次遍历字符串和每次都需要回文验证。
- 空间复杂度:O(1)。
-
动态规划
-
定义状态:dp[i][j] 表示子串 s[i…j] 是否为回文子串,这里子串 s[i…j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。
-
状态转移方程
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1] -
时间空间复杂度: O(n^2)
-
参考:liweiwei1419LeetCode题解:动态规划、中心扩散、Manacher 算法
-
-
中心扩散
- 寻找回文串的问题核心思想是:从中间开始向两边扩散来判断回文串。
- 中心扩散相对好理解,同时三种方法的最优解。建议掌握这种写法。
- 时间O(n^2),空间O(1)。这道题是少有的动态规划非最优解法的问题
- 参考labuladong的算法小炒
三、题解
- 1.暴力匹配
public String longestPalindrome(String s) { //特例处理 回文串的长必须 ≥ 2 int len = s.length(); if(len < 2) return s; //记录最大长度 和起始位置 int maxLength = 1; //长度至少为1 int begin = 0 ; char[] charArray = s.toCharArray(); //枚举所有长度大于等于2 的子串 for(int i = 0; i < len-1; i++){ for(int j = i+1; j < len; j++){ //判断是否是回文串, 基于当前最大长度长度判断 if( j-i+1 > maxLength && isPalindrome(charArray,i,j)){ maxLength = j-i+1 ; begin = i; } } } //根据起始位置 和 最大长度截取字符串 返回 return s.substring(begin,begin+maxLength); } //判断是否是回文串 private boolean isPalindrome(char[] charArray, int left, int right){ while(left < right){ if(charArray[left]!= charArray[right]) return false; left++; right--; } //退出循环 left = right 是回文 return true; }
- 2.动态规划
public String longestPalindrome(String s) { //0.特例处理 int len = s.length(); if(len < 2) return s; //记录最大长度 和起始位置 int maxLength = 1, begin = 0; //1.我是谁? dp[i][j]代表子串s[i,...,j]是否是回文串,包含s[i] 和 s[j] boolean[][] dp = new boolean[len][len]; char[] charArray = s.toCharArray(); //3.初始化 for(int i = 0; i < len; i++) dp[i][i] = true; //2.我从哪里来 dp[i][j] = s[i] == s[j] && dp[i+1]d[j-1] for(int j = 1; j < len; j++){ //注意填表顺序, for(int i = 0; i < j; i++){ if(charArray[i] != charArray[j]) dp[i][j] = false; else{ if( j-i+1 < 4) //当子串的长度为 2 或者 3 的时候肯定是回文串 dp[i][j] = true; else dp[i][j] = dp[i+1][j-1]; } if(dp[i][j] == true && j-i+1 > maxLength){ maxLength = j-i+1; begin = i; } } } return s.substring(begin, begin + maxLength); }
- 3.中心扩散
public String longestPalindrome(String s) { if (s == null || s.length() < 1) return ""; int start = 0, end = 0; for(int i = 0; i < s.length(); i++){ //以s[i]为中心的最长回文串 奇数 int len1 = getPalindrome(s,i,i); //以s[i],s[j]为中心的最长回文串 偶数 int len2 = getPalindrome(s,i,i+1); int len = Math.max(len1,len2); if(len > end - start){ start = i - (len-1)/2; end = i+len/2; } } return s.substring(start, end + 1); } public int getPalindrome(String s,int left, int right){ //防止越界 while(left >=0 && right<s.length() && s.charAt(left) == s.charAt(right)){ left--; right++; } //返回以s[left] 和s[right]为中心的最长回文串 return right - left -1; }