Leetcode 5. 最长回文子串(Longest Palindromic Substring)
推薦理由:暴力解法太 naive,中心擴(kuò)散不普適,Manacher 就更不普適了,是專門解這個(gè)問題的方法。而用動(dòng)態(tài)規(guī)劃我認(rèn)為是最有用的,可以幫助你舉一反三的方法。
補(bǔ)充說明:Manacher 算法有興趣的朋友們可以了解一下,有人就借助它的第一步字符串預(yù)處理思想,解決了 LeetCode 第 4 題。因此以上推薦僅代表個(gè)人觀點(diǎn)。
解決這類 “最優(yōu)子結(jié)構(gòu)” 問題,可以考慮使用 “動(dòng)態(tài)規(guī)劃”:
1、定義 “狀態(tài)”;
2、找到 “狀態(tài)轉(zhuǎn)移方程”。
記號(hào)說明: 下文中,使用記號(hào) s[l, r] 表示原始字符串的一個(gè)子串,l、r 分別是區(qū)間的左右邊界的索引值,使用左閉、右閉區(qū)間表示左右邊界可以取到。舉個(gè)例子,當(dāng) s = 'babad' 時(shí),s[0, 1] = 'ba' ,s[2, 4] = 'bad'。
1、定義 “狀態(tài)”,這里 “狀態(tài)”數(shù)組是二維數(shù)組。
dp[l][r] 表示子串 s[l, r](包括區(qū)間左右端點(diǎn))是否構(gòu)成回文串,是一個(gè)二維布爾型數(shù)組。即如果子串 s[l, r] 是回文串,那么 dp[l][r] = true。
2、找到 “狀態(tài)轉(zhuǎn)移方程”。
首先,我們很清楚一個(gè)事實(shí):
1、當(dāng)子串只包含 11 個(gè)字符,它一定是回文子串;
2、當(dāng)子串包含 2 個(gè)以上字符的時(shí)候:如果 s[l, r] 是一個(gè)回文串,例如 “abccba”,那么這個(gè)回文串兩邊各往里面收縮一個(gè)字符(如果可以的話)的子串 s[l + 1, r - 1] 也一定是回文串,即:如果 dp[l][r] == true 成立,一定有 dp[l + 1][r - 1] = true 成立。
根據(jù)這一點(diǎn),我們可以知道,給出一個(gè)子串 s[l, r] ,如果 s[l] != s[r],那么這個(gè)子串就一定不是回文串。如果 s[l] == s[r] 成立,就接著判斷 s[l + 1] 與 s[r - 1],這很像中心擴(kuò)散法的逆方法。
事實(shí)上,當(dāng) s[l] == s[r] 成立的時(shí)候,dp[l][r] 的值由 dp[l + 1][r - l] 決定,這一點(diǎn)也不難思考:當(dāng)左右邊界字符串相等的時(shí)候,整個(gè)字符串是否是回文就完全由“原字符串去掉左右邊界”的子串是否回文決定。但是這里還需要再多考慮一點(diǎn)點(diǎn):“原字符串去掉左右邊界”的子串的邊界情況。
1、當(dāng)原字符串的元素個(gè)數(shù)為 33 個(gè)的時(shí)候,如果左右邊界相等,那么去掉它們以后,只剩下 11 個(gè)字符,它一定是回文串,故原字符串也一定是回文串;
2、當(dāng)原字符串的元素個(gè)數(shù)為 22 個(gè)的時(shí)候,如果左右邊界相等,那么去掉它們以后,只剩下 00 個(gè)字符,顯然原字符串也一定是回文串。
把上面兩點(diǎn)歸納一下,只要 s[l + 1, r - 1] 至少包含兩個(gè)元素,就有必要繼續(xù)做判斷,否則直接根據(jù)左右邊界是否相等就能得到原字符串的回文性。而“s[l + 1, r - 1] 至少包含兩個(gè)元素”等價(jià)于 l + 1 < r - 1,整理得 l - r < -2,或者 r - l > 2。
綜上,如果一個(gè)字符串的左右邊界相等,以下二者之一成立即可:
1、去掉左右邊界以后的字符串不構(gòu)成區(qū)間,即“ s[l + 1, r - 1] 至少包含兩個(gè)元素”的反面,即 l - r >= -2,或者 r - l <= 2;
2、去掉左右邊界以后的字符串是回文串,具體說,它的回文性決定了原字符串的回文性。
于是整理成“狀態(tài)轉(zhuǎn)移方程”:
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]))
編碼實(shí)現(xiàn)細(xì)節(jié):因?yàn)橐獦?gòu)成子串 l 一定小于等于 r ,我們只關(guān)心 “狀態(tài)”數(shù)組“上三角”的那部分取值。理解上面的“狀態(tài)轉(zhuǎn)移方程”中的 (r - l <= 2 or dp[l + 1, r - 1]) 這部分是關(guān)鍵,因?yàn)?or 是短路運(yùn)算,因此,如果收縮以后不構(gòu)成區(qū)間,那么就沒有必要看繼續(xù) dp[l + 1, r - 1] 的取值。
讀者可以思考一下:為什么在動(dòng)態(tài)規(guī)劃的算法中,不用考慮回文串長度的奇偶性呢。想一想,答案就在狀態(tài)轉(zhuǎn)移方程里面。
具體編碼細(xì)節(jié)在代碼的注釋中已經(jīng)體現(xiàn)。
?
class Solution { public:string longestPalindrome(string s) {if (s.size() < 2) return s;int n = s.size(), maxLen = 0, start = 0;for (int i = 0; i < n - 1; ++i) {searchPalindrome(s, i, i, start, maxLen);searchPalindrome(s, i, i + 1, start, maxLen);}return s.substr(start, maxLen);}void searchPalindrome(string s, int left, int right, int& start, int& maxLen) {while (left >= 0 && right < s.size() && s[left] == s[right]) {--left; ++right;}if (maxLen < right - left - 1) {start = left + 1;maxLen = right - left - 1;}} };?
總結(jié)
以上是生活随笔為你收集整理的Leetcode 5. 最长回文子串(Longest Palindromic Substring)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宫腔粘连试管婴儿成功率有多少
- 下一篇: 【线程】互斥锁