5. 最长回文子串——暴力解法+动态规划+中心扩散

5. 最长回文子串

一、题目

  • 给定一个字符串 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;
    }
    
©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页