JavaScript求解最長(zhǎng)回文子串的方法分享
題目描述
給定一個(gè)字符串 s,找到 s 中最長(zhǎng)的回文子串。你可以假設(shè) s 的最大長(zhǎng)度為 1000。
示例 1:
輸入: "babad" 輸出: "bab" 注意: "aba" 也是一個(gè)有效答案。
示例 2:
輸入: "cbbd" 輸出: "bb"
題解
回文:指一個(gè)正讀和反讀都相同的字符串,例如,“aba” 是回文,而 “abc” 不是。
解決方案
思路一:暴力法
即通過雙重遍歷來獲取目標(biāo)字符串所有的子串,push 到一個(gè)數(shù)組里面,然后根據(jù)字符串長(zhǎng)度排序,從最長(zhǎng)字串開始循環(huán)校驗(yàn),第一個(gè)為回文的子串就是我們要的結(jié)果
復(fù)雜度分析
- 時(shí)間復(fù)雜度:O(n^3)
- 空間復(fù)雜度: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 }
上面代碼在目標(biāo)字符串長(zhǎng)度過大的時(shí)候,會(huì)超出時(shí)間限制,遠(yuǎn)遠(yuǎn)超出O(n^2) 的理想時(shí)間復(fù)雜度,這是因?yàn)檫^多的for 循環(huán),js 自帶函數(shù)使用過多造成的,優(yōu)化一下
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 的測(cè)試,我覺得必須要把 slice split reverse join 這些函數(shù)都干掉才行,也可能 JS 語(yǔ)言效率確實(shí)慢一些
思路二:最長(zhǎng)公共字串
反轉(zhuǎn) S,使之變成 S'。找到 S 和 S' 之間最長(zhǎng)的公共子串,判斷是否是回文
復(fù)雜度分析
- 時(shí)間復(fù)雜度:O(n^2)
- 空間復(fù)雜度: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) }
思路三:中心拓展
遍歷一遍字符串,以每個(gè)字符為中心向兩邊判斷,是否為回文字符串
復(fù)雜度分析
- 時(shí)間復(fù)雜度:O(n^2)
- 空間復(fù)雜度:O(1)
/** * @param {string} s * @return {string} */ var longestPalindrome = function(s) { let size = s.length let start = 0 let len = 0 //字串長(zhǎng)度 // 奇數(shù)長(zhǎng)度的回文字串 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 } } // 偶數(shù)長(zhǎng)度的回文字串 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 算法涉及中心拓展法、動(dòng)態(tài)規(guī)劃,是manacher 1975年發(fā)明出來用來解決最長(zhǎng)回文子串的線性算法
// 第一步 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('參數(shù)錯(cuò)誤,您傳遞的分割字符,在輸入字符串中存在!') } return divide + s.split('').join(divide) + divide } // 輔助數(shù)組 var centerSpread = function(s, center) { // left = right 的時(shí)候,此時(shí)回文中心是一個(gè)空隙,回文串的長(zhǎng)度是奇數(shù) // right = left + 1 的時(shí)候,此時(shí)回文中心是任意一個(gè)字符,回文串的長(zhǎng)度是偶數(shù) 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 算法的工作,就是對(duì)上面代碼中的輔助數(shù)組 p 進(jìn)行優(yōu)化,使時(shí)間復(fù)雜度的降到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]); } // 下一次嘗試擴(kuò)散的左右起點(diǎn),能擴(kuò)散的步數(shù)直接加到 p[i] 中 let left = i - (1 + p[i]); let right = i + (1 + p[i]); // left >= 0 && right < formatSize 保證不越界 // str.charAt(left) == str.charAt(right) 表示可以擴(kuò)散 1 次 while (left >= 0 && right < formatSize && str.charAt(left) == str.charAt(right)) { p[i]++; left--; right++; } // 根據(jù) maxRight 的定義,它是遍歷過的 i 的 i + p[i] 的最大者 // 如果 maxRight 的值越大,進(jìn)入上面 i < maxRight 的判斷的可能性就越大,這樣就可以重復(fù)利用之前判斷過的回文信息了 if (i + p[i] > maxRight) { // maxRight 和 center 需要同時(shí)更新 maxRight = i + p[i]; center = i; } if (p[i] > maxSize) { // 記錄最長(zhǎng)回文子串的長(zhǎng)度和相應(yīng)它在原始字符串中的起點(diǎn) 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('參數(shù)錯(cuò)誤,您傳遞的分割字符,在輸入字符串中存在!') } return divide + s.split('').join(divide) + divide } longestPalindrome('babb')
到此這篇關(guān)于JavaScript求解最長(zhǎng)回文子串的方法分享的文章就介紹到這了,更多相關(guān)JavaScript最長(zhǎng)回文子串內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)基礎(chǔ)排序算法的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何利用JavaScript實(shí)現(xiàn)基礎(chǔ)排序算法,如:冒泡排序、選擇排序、插入排序和快速排序,感興趣的可以了解一下2022-06-06div+css實(shí)現(xiàn)鼠標(biāo)放上去,背景跟圖片都會(huì)變化。
div+css實(shí)現(xiàn)鼠標(biāo)放上去,背景跟圖片都會(huì)變化。...2007-06-06Javascript基于OOP實(shí)實(shí)現(xiàn)探測(cè)器功能代碼實(shí)例
這篇文章主要介紹了Javascript基于OOP實(shí)實(shí)現(xiàn)探測(cè)器功能代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08JS實(shí)現(xiàn)數(shù)組/對(duì)象數(shù)組刪除其中某一項(xiàng)
這篇文章主要介紹了JS實(shí)現(xiàn)數(shù)組/對(duì)象數(shù)組刪除其中某一項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09JS彈出對(duì)話框?qū)崿F(xiàn)方法(三種方式)
這篇文章主要介紹了JS彈出對(duì)話框?qū)崿F(xiàn)方法,結(jié)合實(shí)例形式分析了三種方式,包括alert、confirm及prompt,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-12-12js eval函數(shù)使用,js對(duì)象和字符串互轉(zhuǎn)實(shí)例
下面小編就為大家?guī)硪黄猨s eval函數(shù)使用,js對(duì)象和字符串互轉(zhuǎn)實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03JXTree對(duì)象,讀取外部xml文件數(shù)據(jù),生成樹的函數(shù)
JXTree對(duì)象,讀取外部xml文件數(shù)據(jù),生成樹的函數(shù)...2007-04-04JS實(shí)現(xiàn)隨機(jī)數(shù)生成算法示例代碼
JS實(shí)現(xiàn)隨機(jī)數(shù)生成算法的方法有很多,本文為大家介紹一個(gè)比較不錯(cuò)的方法,代碼如下,感興趣的朋友可以參考下,希望對(duì)大家有所幫助2013-08-08js實(shí)現(xiàn)文本框輸入文字個(gè)數(shù)限制代碼
這篇文章主要介紹了js實(shí)現(xiàn)文本框輸入文字個(gè)數(shù)限制代碼,文本框輸入的文字個(gè)數(shù)并不是無限制的,一般都會(huì)限定一個(gè)輸入最高上限,如何限制,請(qǐng)看本文2015-12-12style、 currentStyle、 runtimeStyle區(qū)別分析
style、 currentStyle、 runtimeStyle區(qū)別分析,需要的朋友可以參考下。2010-08-08