leetcode 5 最長回文子串

2022-09-14 03:57:06 字數 4342 閱讀 8750

目錄

date: 2018-12-27

給定乙個字串s,找到s中最長的回文子串。可以假設s的最大長度為 1000.

示例1:

輸入:"babad"

輸出:"bad"

注意:"abd" 也是乙個有效答案。

示例2:

輸入:"cbbd"

輸出:"bb"

回文是一種文字現象,乙個句子或單詞,正著看和反著看是完全一致的,比如中文有「上海自來水來自海上」、「黃山落葉松葉落山黃」,英語有 'level', 'dad', 'mom', 'bob', 'noon' 等。

這道題的目的就是找出乙個單詞中最長的回文部分,如 banana 中的 anana 就是它的最長回文子串,如果認為乙個單獨的字母也算是回文的話,那麼每個單詞都有長度至少為 1 的回文子串。所以只要輸入的字串s不為空,函式一定會返回長度大於等於 1 的字串,但是知道這點好像也沒有對於解題沒有什麼幫助哦。。。

初見這個問題,就會想到使用暴力法求解:遍歷整個字串,對於每個位置,執行一次palindrome()方法獲取以該位置為中心的最長的回文字,不斷迭代最長回文字串,則該字串便是所求了。

public class bruteforce_origin 

string res = "";

for (int i = 0; i < s.length; i++)

return res;

}private static string palindrome(string s, int center)

i = center;

j = center + 1;

while (i >= 0 && j < s.length() && s.charat(i) == s.charat(j))

return res;}}

這個方法理解起來沒什麼難度,相應地效率也非常低。在 leetcode上提交的結果是:

在此方法中,我們不斷建立新的string物件,這時非常耗時的,**執行的三百毫秒中,至少有二百多毫秒在操作字串。所以我們不難想出改進方案——使用陣列,傳遞陣列的引用,palindrom()計算回文字開始與結束的下標,兩個int型別的維護成本比操作字串不知道低到**去了。

public class bruteforce 

char str = s.tochararray();

int res = ;

for (int i = 0; i < str.length; i++)

return s.substring(res[0], res[1]+1);

}// 獲取回文子串的區間端點下標

private static int parlindrome(char str, int center)

i++;

j--;

int m = center, n = center + 1;

while (m >= 0 && n < str.length && str[m] == str[n])

return new int ;}}

不出所料,這段**的效率提高的不是一星半點兒:

執行時間縮短了乙個數量級,與方法一的**對比就不難看出物件的操作和直接操作基本資料型別在時間上的差別了。

這個「馬拉車」演算法名字很奇怪吼,是哪位馬車伕在工作之餘喝茶時一拍腦袋想出的嗎?並不是這樣的,只是因為它的發明者名叫 manacher,演算法便被命名為 manachers algorithm,音譯成中文後就變成了奇怪的「馬拉車」了。

你若有興趣,可以看看最大回文子串的維基頁面,裡面就有講到這個馬拉車演算法,我也是從這裡學來的。要是得我解釋得太不清楚,你也可以讀一讀這個頁面。

回文字有乙個比較惱人的特點,就是當從中點開始向兩邊遍歷時,偶數和奇數長度回文的情況是不同的,必須得為兩種情況分別程式設計,去扣邊界。比如,對於s = "bob"中點的下標是 1,從中間向兩邊擴充套件就是s[0] == s[1];而對於s = "noon",中點下標是 1 和 2,兩個都是中點,向兩邊擴充套件就變成了s[0] == s[3]。解法一和二中palindrome()方法執行兩輪迭代就是處於這個原因——我們無法知道在i

處的回文字長度到底是偶數還是奇數,所以必須得假設兩種情況分別成立,計算出兩個長度,取最大值作為返回值。

那麼有沒有什麼可以統一兩種情況的方法呢?答案自然是有的,給出答案的人就是我們這位「馬拉車」大佬了,他給出的方法是擴張字串s,在每個字元兩側加上佔位符,在這裡我們使用+作為佔位符,這樣s就發生了一點微妙的變化——當s = bob(奇數長度)時,加上佔位符後 s =+b+o+b+,它是關於s[3] => o對稱的;而當 s =noon(偶數長度)時,加上佔位符 s =+n+o+o+n+,它是關於s[4] => +對稱的。就是這樣,加上佔位符之後,s 的長度不論奇偶,都有了唯一確定的中點,也就是說, s 的長度變成了奇數。這樣,我們就可以合併兩種情況。

回文最重要的性質就是——對稱,利用這一點,我們不難推出回文中乙個位置i上以i為中心的回文子串的長度和以回文中心c為軸i的對稱點2*c-i處相同。

我們可以利用此特性減少**的工作量,只需要借助乙個輔助陣列記錄 s 中對應下標的回文長度。

有這兩個工具,我們的**就沒什麼想不通的了。

public class manachersalgorithm 

char schar = addboundaries(s.tochararray());

int p = new int[schar.length];

int c = 0;

int r = 0;

int i = 0, j = 0;

for (int n = 1; n < schar.length; n++) else else

}while (i >= 0 && j < p.length && schar[i] == schar[j])

if (i + p[n] > r)

}int len = 0;

c = 0;

for (int n = 0; n < p.length; n++)

}char res = arrays.copyofrange(schar, c - len, c + len + 1);

return new string(removeboundaries(res));

}private static char addboundaries(char str) ;

}char res = new char[str.length*2 + 1];

int p = 0;

res[p++] = '+';

for (int i = 0; i < str.length; i++)

return res;

}private static char removeboundaries(char str) ;

}char res = new char[(str.length - 1) / 2];

for (int i = 0; i < res.length; i++)

return res;}}

如果對於說這段**不太好理解,你可以使用 print **。

提交 leetcode 運**況為

雖然是 18 ms,看起來比解法二沒快多少,但是要知道,這個演算法的複雜度是 o(n) 的,和之前兩個演算法不是乙個量級的。

LeetCode5最長回文子串

給定乙個字串s,找到s中最長的回文子串。你可以假設s長度最長為1000。示例 輸入 babad 輸出 bab 注意 aba 也是有效答案示例 輸入 cbbd 輸出 bb 動態規劃來做,每個回文字串的子字串也是回文字串,即string是回文字串那麼它的string.substring 1,lenth ...

LeetCode 5 最長回文子串

問題描述 給定乙個字串s,找到s中最長的回文子串。你可以假設s的最大長度為1000。示例 1 輸入 babad 輸出 bab 注意 aba 也是乙個有效答案。示例 2 輸入 cbbd 輸出 bb 解決方案 中心擴充套件演算法 事實上,只需使用恆定的空間,我們就可以在 o n 2 的時間內解決這個問題...

leetcode5 最長回文子串

遞推式 1 一般 s i 1 s j 1 and j i and j i len s i 1,j 1 2 初始化dp矩陣對角線的值為 true,相鄰兩個元素相等時dp i i 1 為true 初始化回文串起始位置和長度。def longestpalindrome s n len s if s ret...