LeetCode 5「最長回文子串」:給定字符串 s (長度 ≤ 1000),求最長回文子串。本文介紹動態規劃解法:dp[i][j] 表示 s[i..j] 是否為回文,利用狀態轉移避免重複判斷。
dp[i][j] = true 當 s[i..j] 是回文,其中 0 ≤ i ≤ j < n。
dp[i][i] = true。dp[i][i-1] = true (方便長度 2 判斷)。false。s[i..j] 是回文 ⇔ s[i] == s[j] 且 s[i+1..j-1] 是回文。
即:dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]。
按子串長度 len = 2..n 遞增,內層遍歷起點 i = 0..n-len,j = i+len-1。
此時 dp[i+1][j-1] (長度 len-2) 已計算。
維護 maxLen 和 start,更新時記錄。
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n <= 1) return s;
vector<vector<bool>> dp(n, vector<bool>(n, false));
int start = 0, maxLen = 1;
// 長度 1
for (int i = 0; i < n; ++i) dp[i][i] = true;
// 長度 0 輔助
for (int i = 1; i < n; ++i) dp[i][i-1] = true;
for (int len = 2; len <= n; ++len) {
for (int i = 0; i + len <= n; ++i) {
int j = i + len - 1;
if (s[i] == s[j] && dp[i+1][j-1]) {
dp[i][j] = true;
if (len > maxLen) {
maxLen = len;
start = i;
}
}
}
}
return s.substr(start, maxLen);
}
};
枚舉回文中心(2n-1 個:字符位和字符間隙),向兩側擴展。
代碼更短、空間更優,面試常作為進階解法。
string longestPalindrome(string s) {
int n = s.size(), start = 0, maxLen = 1;
auto expand = [&](int l, int r) {
while (l >= 0 && r < n && s[l] == s[r]) {
if (r - l + 1 > maxLen) { start = l; maxLen = r - l + 1; }
--l; ++r;
}
};
for (int i = 0; i < n; ++i) {
expand(i, i); // 奇數長度
expand(i, i+1); // 偶數長度
}
return s.substr(start, maxLen);
}
利用回文對稱性避免重複擴展,線性掃描。代碼較複雜,面試中了解原理即可,通常不要求默寫。
最長回文子串三大解法:
| 方法 | 時間 | 空間 | 適用場景 |
|---|---|---|---|
| 動態規劃 | O(n²) | O(n²) | 思路直觀,易於理解狀態轉移 |
| 中心擴展 | O(n²) | O(1) | 面試首選,代碼簡潔高效 |
| Manacher | O(n) | O(n) | 極大 n (10⁵+),競賽進階 |
建議掌握中心擴展法作為主力,動態規劃作為理解遞推關係的基礎,Manacher 作為拓展視野。