• 0005. Longest Palindromic Substring (M)


    Longest Palindromic Substring (M)

    题目

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

    Example 1:

    Input: "babad"
    Output: "bab"
    Note: "aba" is also a valid answer.
    

    Example 2:

    Input: "cbbd"
    Output: "bb"
    

    题意

    给定一个字符串s,要求输出该字符串中的最大回文子串。

    思路

    1. 暴力法。复杂度(O(N^3))
    2. 动态规划法。设若P[i][j]==true,则字符串s中从下标i到下标j构成的子串为回文子串,则很容易得到状态转移方程及边界条件如下,复杂度为(O(N^2))

    [P[i][j] = (P[i+1][j-1] && s[i] == s[j]) ]

    [P[i][j] = egin{cases} true, &i==j\ true, &j==i+1 && s[i]==s[j]\ false, &j==i+1 && s[i] !=s[j] end{cases} ]

    1. 中间扩展法。遍历字符串的每一个字符,奇数子串以该字符为中心向两边扩展搜索,直到两端字符不相同;偶数子串以该字符与下一个字符为中心向两遍扩展搜索,直到两端字符不相同。复杂度为(O(N^2))
    2. 马拉车算法 - Manacher's Algorithm,复杂度为(O(N))

    代码实现

    Java

    动态规划

    class Solution {
        public String longestPalindrome(String s) {
            // 特殊情况排除
            if (s == null || s.isEmpty()) {
                return "";
            }
            
            int maxLen = 0;
            int left = 0;
            int right = 0;
            boolean[][] P = new boolean[s.length()][s.length()];
            
            // 以子串长度及开始位置的变化进行循环
            for (int len = 1; len <= s.length(); len++) {
                for (int i = 0; i + len - 1 < s.length(); i++) {
                    int j = i + len - 1;
                    // 根据状态转移方程及边界条件生成数组P的值
                    if (len == 1) {
                        P[i][j] = true;
                    } else if (len == 2) {
                        P[i][j] = (s.charAt(i) == s.charAt(j));
                    } else {
                        P[i][j] = (P[i + 1][j - 1] && s.charAt(i) == s.charAt(j));
                    }
                    if (P[i][j] && len > maxLen) {
                        maxLen = len;
                        left = i;
                        right = j;
                    }
                }
            }
            return s.substring(left, right + 1);
        }
    }
    

    中间扩展

    class Solution {
        public String longestPalindrome(String s) {
            // 特殊情况处理
            if (s == null || s.isEmpty()) {
                return "";
            }
            
            int maxLen = 0;
            int left = 0;
            int right = 0;
            
            for (int i = 0; i < s.length(); i++) {
                // 奇数子串情况
                int len1 = fromCenter(i, i, s);
                // 偶数子串情况
                int len2 = fromCenter(i, i + 1, s);
                int len = Math.max(len1, len2);
                if (len > maxLen) {
                    maxLen = len;
                    left = i - (len - 1) / 2;
                    right = i + len / 2;
                }
            }
            return s.substring(left, right + 1);
        }
    
        // 从目标字符向两边扩展,返回长度
        public int fromCenter(int i, int j, String s) {
            while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
                i--;
                j++;
            }
            return j - i - 1;
        }
    }
    

    Manacher

    class Solution {
        public String longestPalindrome(String s) {
            // 特殊情况排除
            if (s == null || s.isEmpty()) {
                return "";
            }
    
            String t = transform(s);
            int[] p = new int[t.length()];
            // 中心位置
            int C = 0;
            // 右边界
            int R = 0;
            // 回文串最大半径
            int maxLen = 0;
            // 最长回文串对应中心
            int pos = 0;
    
            for (int i = 0; i < t.length(); i++) {
                // i关于C的对称点
                int j = 2 * C - i;
                
                // 分三种情况进行赋值
                p[i] = R >= i ? Math.min(p[j], R - i) : 0;
                
                // 回文半径可增加的情况
                while (i + p[i] + 1 < t.length() && i - p[i] - 1 >= 0 
                        && t.charAt(i + p[i] + 1) == t.charAt(i - p[i] - 1)) {
                    p[i]++;
                }
    
                if (p[i] > maxLen) {
                    maxLen = p[i];
                    pos = i;
                }
                
                // 更新中心点和右边界
                if (i + p[i] > R) {
                    C = i;
                    R = i + p[i];
                }
            }
    
            int left = (pos - maxLen) / 2;
            int right = (pos + maxLen - 1) / 2;
            return s.substring(left, right + 1);
        }
    
        // 字符串转换
        public String transform(String s) {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < s.length(); i++) {
                builder.append('#').append(s.charAt(i));
            }
            builder.append('#');
            return builder.toString();
        }
    }
    

    JavaScript

    动态规划

    /**
     * @param {string} s
     * @return {string}
     */
    var longestPalindrome = function (s) {
      if (!s.length) return ''
    
      let max = 0
      let ends = [0, 0]
      let isPalin = []
      for (let i = 0; i < s.length; i++) {
        isPalin[i] = []
        isPalin[i][i] = true
      }
    
      for (let len = 2; len <= s.length; len++) {
        for (let i = 0; i + len - 1 < s.length; i++) {
          let j = i + len - 1
          if (s[i] !== s[j]) {
            isPalin[i][j] = false
          } else {
            isPalin[i][j] = len === 2 ? true : isPalin[i + 1][j - 1]
          }
          if (isPalin[i][j] && j - i + 1 > max) {
            max = j - i + 1
            ends = [i, j]
          }
        }
      }
    
      return s.slice(ends[0], ends[1] + 1)
    }
    

    中间扩展

    /**
     * @param {string} s
     * @return {string}
     */
    var longestPalindrome = function (s) {
      let max = 0
      let ends = []
    
      for (let i = 0; i < s.length; i++) {
        let len = Math.max(findLength(s, i, i), findLength(s, i, i + 1))
        if (len > max) {
          max = len
          ends = [i - Math.floor((len - 1) / 2), i + Math.floor(len / 2)]
        }
      }
    
      return s.slice(ends[0], ends[1] + 1)
    }
    
    let findLength = function (s, i, j) {
      while (i >= 0 && j < s.length && s[i] === s[j]) {
        i--
        j++
      }
      return j - i - 1
    }
    

    Manacher

    /**
     * @param {string} s
     * @return {string}
     */
    var longestPalindrome = function (s) {
      if (!s.length) return ''
      let ends = manacher(transform(s))
      return s.slice(ends[0], ends[1] + 1)
    }
    
    let manacher = function (s) {
      let maxRadius = -1
      let center = 0
      let right = 0
      let radius = new Array(s.length).fill(0)
      let ends = []
    
      while (right < s.length) {
        let left = 2 * center - right
        radius[right] = right > center + radius[center] ? 0 : Math.min(radius[left], center + radius[center] - right)
        while (
          right + radius[right] + 1 < s.length &&
          right - radius[right] - 1 >= 0 &&
          s[right + radius[right] + 1] === s[right - radius[right] - 1]
        ) {
          radius[right]++
        }
        if (radius[right] > maxRadius) {
          maxRadius = radius[right]
          ends = [right - radius[right], right + radius[right]]
        }
        if (right + radius[right] > center + radius[center]) {
          center = right
        }
        right++
      }
    
      return [Math.floor(ends[0] / 2), Math.floor(ends[1] / 2) - 1]
    }
    
    let transform = function (s) {
      let t = '#'
      for (let c of s) {
        t += c + '#'
      }
      return t
    }
    

    参考 - Manacher

    博客园 - BIT祝威

  • 相关阅读:
    poj 2528 Mayor's posters (线段树+离散化)
    poj 1201 Intervals (差分约束)
    hdu 4109 Instrction Arrangement (差分约束)
    poj 1195 Mobile phones (二维 树状数组)
    poj 2983 Is the Information Reliable? (差分约束)
    树状数组 讲解
    poj 2828 Buy Tickets (线段树)
    hdu 1166 敌兵布阵 (树状数组)
    Ubuntu网络配置
    Button控制窗体变量(开关控制灯的状态)
  • 原文地址:https://www.cnblogs.com/mapoos/p/13129889.html
Copyright © 2020-2023  润新知