zoukankan      html  css  js  c++  java
  • 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祝威

  • 相关阅读:
    数字视频基础(四) 分类: 生活百科 2014-07-01 11:19 388人阅读 评论(0) 收藏
    数字视频基础(三) 分类: 生活百科 2014-07-01 11:19 357人阅读 评论(0) 收藏
    数字视频基础(二) 分类: 生活百科 2014-07-01 11:18 398人阅读 评论(0) 收藏
    数字视频基础(一) 分类: 生活百科 2014-07-01 11:17 345人阅读 评论(0) 收藏
    PCI-X总线 分类: 浅谈PCI 2014-07-01 08:28 697人阅读 评论(2) 收藏
    Mfc Tips
    windows 桌面开发 (zz)
    备 忘
    编程坑爹的注意
    虚拟机安装后的基本操作
  • 原文地址:https://www.cnblogs.com/mapoos/p/13129889.html
Copyright © 2011-2022 走看看