<
>

JavaScript求解最长回文子串的方法分享

2022-08-05 17:44:10 来源:易采站长站 作者:

目录
题目描述题解解决方案思路一:暴力法思路二:最长公共字串思路三:中心拓展思路四:Manacher 算法

题目描述

给定一个字符串>

示例 1:

输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd" 输出: "bb"

题解

回文:指一个正读和反读都相同的字符串,例如,“aba”>

解决方案

思路一:暴力法

即通过双重遍历来获取目标字符串所有的子串,push>

复杂度分析

    时间复杂度:O(n^3)空间复杂度:O(1)
    /**
     * @param {string} s
     * @return {string}
     */
    var longestPalindrome = function(s) {
        let m = []
        let res = ''
        for(let i=0; i<s.length; i++) {
            for(let j = i; j < s.length; j++) {
                m.push(s.slice(i, j+1))
            }
        }
        m.sort(function(a,b){
            return b.length - a.length
        })
        for (let i of m) {
            if (i === i.split('').reverse().join('')) {
                res = i
                break;
            }
        }
        return res
    }

    上面代码在目标字符串长度过大的时候,会超出时间限制,远远超出O(n^2) 的理想时间复杂度,这是因为过多的for 循环,js 自带函数使用过多造成的,优化一下

    var longestPalindrome = function(s) {
        let m = []
        let res = ''
        for(let i=0; i<s.length; i++) {
            for(let j = i; j < s.length; j++) {
                if (s[i] != s[j]) break
                let ele = s.slice(i, j+1)
                if (ele === ele.split('').reverse().join('') && ele.length > res.length) {
                    res = ele
                }
            }
        }
        return res
    }

    看起来好多了,但是依然通不过Leecode 的测试,我觉得必须要把 slice split reverse join 这些函数都干掉才行,也可能 JS 语言效率确实慢一些

    思路二:最长公共字串

    反转>

    复杂度分析

      时间复杂度:O(n^2)空间复杂度:O(n^2)
      /**
       * @param {string} s
       * @return {string}
       */
      var longestPalindrome = function(s) {
          let rs = s.split('').reverse().join('')
          let size = s.length
          let len = 0
          let end = 0
          let a = new Array(size)
          for (let i = 0; i < size; i++) {
              a[i] = new Array()
          }
          for (let i = 0; i < size; i++) {
              for(let j = 0; j < size; j++) {
                  if (s[i] === rs[j]) {
                      if (i > 0 && j > 0) {
                          a[i][j] = a[i-1][j-1] + 1
                      } else {
                          a[i][j] = 1
                      }
      
                      if(a[i][j] > len) {
                          let beforeIndex = size - 1 - j
                          if (beforeIndex + a[i][j] -1 == i) { 
                              len = a[i][j]
                              end = i
                          }
                      }
                  } else {
                      a[i][j] = 0
                  }
              }
          }
          return s.slice(end-len+1, end+1)
      }

      思路三:中心拓展

      遍历一遍字符串,以每个字符为中心向两边判断,是否为回文字符串

      复杂度分析

        时间复杂度:O(n^2)空间复杂度:O(1)
        /**
         * @param {string} s
         * @return {string}
         */
        var longestPalindrome = function(s) {
            let size = s.length
            let start = 0
            let len = 0 //字串长度
            // 奇数长度的回文字串
            for (let i = 0; i < size; i++) {
                let left = i - 1
                let right = i + 1
                while (left >= 0 && right < size && s[left] == s[right]) {
                    left --
                    right ++
                }
                if (right - left - 1 > len) {
                    start = left +1
                    len = right -left -1
                }
            }
            // 偶数长度的回文字串
            for (let i = 0; i < size; i++) {
                let left = i
                let right = i + 1
                while (left >= 0 && right < size && s[left] == s[right]) {
                    left--
                    right++
                }
                if (right -left -1 > len) {
                    start = left + 1
                    len = right -left -1
                }
            }
            return s.slice(start, start + len)
        }

        思路四:Manacher>

        manacher 算法涉及中心拓展法、动态规划,是manacher 1975年发明出来用来解决最长回文子串的线性算法

        // 第一步
        var longestPalindrome = function(s) {
            let size = s.length
            if (size < 2) {
                return s
            }
            let str = addBoundaries(s, '#')
            let formatSize = 2 * size +1
            let maxSize = 1
        
            let start = 0
            for (let i=0; i<formatSize; i++) {
                let curSize = centerSpread(str, i)
                if (curSize > maxSize) {
                    maxSize = curSize
                    start = (i - maxSize)/2
                }
            }
            return s.slice(start, start + maxSize)
        }
        
        // 处理原字符串
        var addBoundaries = function(s, divide) {
            let size = s.length
            if (size === 0) {
                return ''
            }
            if (s.indexOf(divide) != -1) {
                throw new Error('参数错误,您传递的分割字符,在输入字符串中存在!')
            }
            return divide + s.split('').join(divide) + divide
        }
        
        // 辅助数组
        var centerSpread = function(s, center) {
             // left = right 的时候,此时回文中心是一个空隙,回文串的长度是奇数
            // right = left + 1 的时候,此时回文中心是任意一个字符,回文串的长度是偶数
            let len = s.length
            let i = center - 1
            let j = center + 1
            let step = 0
            while (i >= 0 && j < len && s.charAt(i) == s.charAt(j)) {
                i--
                j++
                step++
            }
            return step
        }
        longestPalindrome('ababadfglldf;hk;lhk')

        manacher 算法的工作,就是对上面代码中的辅助数组 p 进行优化,使时间复杂度的降到O(n^2)

        // 完整
        var longestPalindrome = function(s) {
            let size = s.length
            if (size < 2) {
                return s
            }
            let str = addBoundaries(s, '#')
            let formatSize = 2 * size +1
        
            let p = new Array(formatSize).fill(0)
        
            let maxRight = 0
            let center = 0
        
            let maxSize = 1
        
            let start = 0
            for (let i=0; i<formatSize; i++) {
                if (i < maxRight) {
                    let mirror = 2 * center - i;
                    // Manacher 算法的核心
                    p[i] = Math.min(maxRight - i, p[mirror]);
                }
                // 下一次尝试扩散的左右起点,能扩散的步数直接加到 p[i] 中
                let left = i - (1 + p[i]);
                let right = i + (1 + p[i]);
        
                // left >= 0 && right < formatSize 保证不越界
                // str.charAt(left) == str.charAt(right) 表示可以扩散 1 次
                while (left >= 0 && right < formatSize && str.charAt(left) == str.charAt(right)) {
                    p[i]++;
                    left--;
                    right++;
        
                }
                // 根据 maxRight 的定义,它是遍历过的 i 的 i + p[i] 的最大者
                // 如果 maxRight 的值越大,进入上面 i < maxRight 的判断的可能性就越大,这样就可以重复利用之前判断过的回文信息了
                if (i + p[i] > maxRight) {
                    // maxRight 和 center 需要同时更新
                    maxRight = i + p[i];
                    center = i;
                }
                if (p[i] > maxSize) {
                    // 记录最长回文子串的长度和相应它在原始字符串中的起点
                    maxSize = p[i];
                    start = (i - maxSize) / 2;
                }
            }
            return s.slice(start, start + maxSize)
        }
        
        var addBoundaries = function(s, divide) {
            let size = s.length
            if (size === 0) {
                return ''
            }
            if (s.indexOf(divide) != -1) {
                throw new Error('参数错误,您传递的分割字符,在输入字符串中存在!')
            }
            return divide + s.split('').join(divide) + divide
        }
        longestPalindrome('babb')

        到此这篇关于JavaScript求解最长回文子串的方法分享的文章就介绍到这了,更多相关JavaScript最长回文子串内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!

暂时禁止评论

微信扫一扫

易采站长站微信账号