JavaScript求解最長回文子串的方法分享
題目描述
給定一個字符串 s,找到 s 中最長的回文子串。你可以假設(shè) s 的最大長度為 1000。
示例 1:
輸入: "babad" 輸出: "bab" 注意: "aba" 也是一個有效答案。
示例 2:
輸入: "cbbd" 輸出: "bb"
題解
回文:指一個正讀和反讀都相同的字符串,例如,“aba” 是回文,而 “abc” 不是。
解決方案
思路一:暴力法
即通過雙重遍歷來獲取目標(biāo)字符串所有的子串,push 到一個數(shù)組里面,然后根據(jù)字符串長度排序,從最長字串開始循環(huán)校驗,第一個為回文的子串就是我們要的結(jié)果
復(fù)雜度分析
- 時間復(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)字符串長度過大的時候,會超出時間限制,遠(yuǎn)遠(yuǎn)超出O(n^2) 的理想時間復(fù)雜度,這是因為過多的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 的測試,我覺得必須要把 slice split reverse join 這些函數(shù)都干掉才行,也可能 JS 語言效率確實慢一些
思路二:最長公共字串
反轉(zhuǎn) S,使之變成 S'。找到 S 和 S' 之間最長的公共子串,判斷是否是回文
復(fù)雜度分析
- 時間復(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)
}思路三:中心拓展
遍歷一遍字符串,以每個字符為中心向兩邊判斷,是否為回文字符串
復(fù)雜度分析
- 時間復(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 //字串長度
// 奇數(shù)長度的回文字串
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ù)長度的回文字串
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 算法涉及中心拓展法、動態(tài)規(guī)劃,是manacher 1975年發(fā)明出來用來解決最長回文子串的線性算法
// 第一步
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ù)錯誤,您傳遞的分割字符,在輸入字符串中存在!')
}
return divide + s.split('').join(divide) + divide
}
// 輔助數(shù)組
var centerSpread = function(s, center) {
// left = right 的時候,此時回文中心是一個空隙,回文串的長度是奇數(shù)
// right = left + 1 的時候,此時回文中心是任意一個字符,回文串的長度是偶數(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 算法的工作,就是對上面代碼中的輔助數(shù)組 p 進(jìn)行優(yōu)化,使時間復(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]);
}
// 下一次嘗試擴散的左右起點,能擴散的步數(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) 表示可以擴散 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 需要同時更新
maxRight = i + p[i];
center = i;
}
if (p[i] > maxSize) {
// 記錄最長回文子串的長度和相應(yīng)它在原始字符串中的起點
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ù)錯誤,您傳遞的分割字符,在輸入字符串中存在!')
}
return divide + s.split('').join(divide) + divide
}
longestPalindrome('babb')到此這篇關(guān)于JavaScript求解最長回文子串的方法分享的文章就介紹到這了,更多相關(guān)JavaScript最長回文子串內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
你可能不知道的JavaScript的new Function()方法
JavaScript的精神領(lǐng)袖Douglas Crockford曾說過JavaScript是程序員唯一不需要學(xué)習(xí)就能直接使用的語言. 在編程中確實是如此2014-04-04
ES6中async函數(shù)與await表達(dá)式的基本用法舉例
async和await是我們進(jìn)行Promise時的一個語法糖,async/await為了讓我們書寫代碼時更加流暢,增強了代碼的可讀性,下面這篇文章主要給大家介紹了關(guān)于ES6中async函數(shù)與await表達(dá)式的基本用法,需要的朋友可以參考下2022-07-07

