最長回文子串

2022-05-07 11:12:10 字數 3150 閱讀 5003

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

示例 1:

輸入: "babad"

輸出: "bab"

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

示例 2:

輸入: "cbbd"

輸出: "bb"

思路:中心擴散法的想法很簡單:遍歷每乙個索引,以這個索引為中心,利用「回文串」中心對稱的特點,往兩邊擴散,看最多能擴散多遠。要注意乙個細節:回文串的長度可能是奇數,也可能是偶數。

我們完全可以設計乙個方法,相容以上兩種情況:

1、如果傳入重合的索引編碼,進行中心擴散,此時得到的最長回文子串的長度是奇數;

2、如果傳入相鄰的索引編碼,進行中心擴散,此時得到的最長回文子串的長度是偶數。

class

solution

}return s.substring(start,end+1

); }

public

static

int expand(string s,int i,int

j)

return j - i - 1

; }

}

其實解決這類 「最優子結構」 問題,我們可以考慮使用 「動態規劃」下來說一下動歸解決該題:

1、定義 「狀態」;

2、找到 「狀態轉移方程」。

記號說明: 下文中,使用記號 s[l, r] 表示原始字串的乙個子串,l、r 分別是區間的左右邊界的索引值,使用左閉、右閉區間表示左右邊界可以取到。舉個例子,當 s = 'babad' 時,s[0, 1] = 'ba' ,s[2, 4] = 'bad'。

1、定義 「狀態」,這裡 「狀態」陣列是二維陣列。

dp[l][r] 表示子串 s[l, r](包括區間左右端點)是否構成回文串,是乙個二維布林型陣列。即如果子串 s[l, r] 是回文串,那麼 dp[l][r] = true。

2、找到 「狀態轉移方程」。

首先,我們很清楚乙個事實:

1、當子串只包含 11 個字元,它一定是回文子串;

2、當子串包含 2 個以上字元的時候:如果 s[l, r] 是乙個回文串,例如 「abccba」,那麼這個回文串兩邊各往裡面收縮乙個字元(如果可以的話)的子串 s[l + 1, r - 1] 也一定是回文串,即:如果 dp[l][r] == true 成立,一定有 dp[l + 1][r - 1] = true 成立。

根據這一點,我們可以知道,給出乙個子串 s[l, r] ,如果 s[l] != s[r],那麼這個子串就一定不是回文串。如果 s[l] == s[r] 成立,就接著判斷 s[l + 1] 與 s[r - 1],這很像中心擴散法的逆方法。

事實上,當 s[l] == s[r] 成立的時候,dp[l][r] 的值由 dp[l + 1][r - l] 決定,這一點也不難思考:當左右邊界字串相等的時候,整個字串是否是回文就完全由「原字串去掉左右邊界」的子串是否回文決定。但是這裡還需要再多考慮一點點:「原字串去掉左右邊界」的子串的邊界情況。

1、當原字串的元素個數為 33 個的時候,如果左右邊界相等,那麼去掉它們以後,只剩下 11 個字元,它一定是回文串,故原字串也一定是回文串;

2、當原字串的元素個數為 22 個的時候,如果左右邊界相等,那麼去掉它們以後,只剩下 00 個字元,顯然原字串也一定是回文串。

把上面兩點歸納一下,只要 s[l + 1, r - 1] 至少包含兩個元素,就有必要繼續做判斷,否則直接根據左右邊界是否相等就能得到原字串的回文性。而「s[l + 1, r - 1] 至少包含兩個元素」等價於 l + 1 < r - 1,整理得 l - r < -2,或者 r - l > 2。

綜上,如果乙個字串的左右邊界相等,以下二者之一成立即可: 1、去掉左右邊界以後的字串不構成區間,即「 s[l + 1, r - 1] 至少包含兩個元素」的反面,即 l - r >= -2,或者 r - l <= 2; 2、去掉左右邊界以後的字串是回文串,具體說,它的回文性決定了原字串的回文性。

dp[l, r] = (s[l] == s[r] and (l - r >= -2 or dp[l + 1, r - 1]))

或者

dp[l, r] = (s[l] == s[r] and (r - l <= 2 or dp[l + 1, r - 1]))

編碼實現細節:因為要構成子串 l 一定小於等於 r ,我們只關心 「狀態」陣列「上三角」的那部分取值。理解上面的「狀態轉移方程」中的 (r - l <= 2 or dp[l + 1, r - 1]) 這部分是關鍵,因為 or 是短路運算,因此,如果收縮以後不構成區間,那麼就沒有必要看繼續 dp[l + 1, r - 1] 的取值。

讀者可以思考一下:為什麼在動態規劃的演算法中,不用考慮回文串長度的奇偶性呢。想一想,答案就在狀態轉移方程裡面。

具體編碼細節在**的注釋中已經體現。

public

class

solution

int longestpalindrome = 1

; string longestpalindromestr = s.substring(0, 1

); boolean dp = new

boolean[len][len];

//abcdedcba

//l r

//如果 dp[l, r] = true 那麼 dp[l + 1, r - 1] 也一定為 true

//關鍵在這裡:[l + 1, r - 1] 一定至少有 2 個元素才有判斷的必要

//因為如果 [l + 1, r - 1] 只有乙個元素,不用判斷,一定是回文串

//如果 [l + 1, r - 1] 表示的區間為空,不用判斷,也一定是回文串

//[l + 1, r - 1] 一定至少有 2 個元素 等價於 l + 1 < r - 1,即 r - l > 2

//寫**的時候這樣寫:如果 [l + 1, r - 1] 的元素小於等於 1 個,即 r - l <= 2 ,就不用做判斷了

//因為只有 1 個字元的情況在最開始做了判斷

//左邊界一定要比右邊界小,因此右邊界從 1 開始

for (int r = 1; r < len; r++) }}

}return

longestpalindromestr;

}}

最長回文子串 最長回文子串行

1.最長回文子串行 可以不連續 include include include include using namespace std 遞迴方法,求解最長回文子串行 intlps char str,int i,int j intmain include include include using n...

最長回文子串

描述 輸入乙個字串,求出其中最長的回文子串。子串的含義是 在原串連續出現的字串片段。回文的含義是 正著看和倒著看是相同的,如abba和abbebba。在判斷是要求忽略所有的標點和空格,且忽略大小寫,但輸出時按原樣輸出 首尾不要輸出多餘的字串 輸入字串長度大於等於1小於等於5000,且單獨佔一行 如果...

最長回文子串

輸入乙個字元,求出其中最長的回文子串。子串的含義是 在元串中連續出現的字串片段。回文的含義是 正看和倒看相同,如abba和yyxyy,在判斷時候應該忽略所有的空格和標點符號,且忽略大小寫,但輸出應該保持原樣,輸入的字元長度不超過5000,且佔據單獨一行,輸出最長的回文子串 如有多個,輸出,起始位置最...