題目鏈接
https://leetcode-cn.com/problems/longest-palindromic-substring/
題目描述
給定一個字符串 s
,找到 s
中最長的回文子串。你可以假設 s
的最大長度為 1000。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。
示例 2:
輸入: "cbbd"
輸出: "bb"
解題方案
思路
- 標簽:動態規劃。
- 動態規劃:通過子問題答案來解決大問題答案;子問題都需要是離散且不依賴其他子問題(詳可見《算法圖解》第九章「動態規劃」)。
- 回文字符串:正讀和反讀都一樣,
""
、"a"
、"aa"
、"aba"
、"abcba"
等。 - 可將字符串
S
反轉為S'
,通過S
和S'
比較,利用動態規劃先得到 最長公共子串。 -
S
和S'
分別置于橫、縱坐標,通過網格(二維數組)一一比較子串(每一個子串就是一個子問題),當前公共子串的長度等于前面公共子串的長度加一,網格記錄當前公共子串的長度。 - 通過最長公共子串的長度和結束位置,得到最長公共子串。
-
S = aacdecaa
和S' = aacedcaa
最長公共子串為acc
,不是回文子串,需要進一步判斷最長公共子串是否為回文子串。 -
caa
為aac
的反向副本,通過檢查反向子串aac
的原始索引是否與子串aac
索引相同,來排除反向副本的情況。 - 不需要檢查反向子串
aac
的每個字符,只需要檢查反向子串末尾字符c
的原始索引,是否等于子串aac
的首字符a
索引即可。 - 時間復雜度:二維數組:O(n^2)。
代碼 1:最長公共子串
class Solution {
public String longestPalindrome(String s) {
if (s.equals("")) {
return "";
}
int length = s.length();
String reversal = new StringBuffer(s).reverse().toString(); // 反轉字符串
int[][] cell = new int[length][length];
int maxLen = 0; // 最長公共子串長度
int maxEnd = 0; // 最長公共子串結束位置
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (reversal.charAt(i) == s.charAt(j)) {
if (i == 0 || j == 0) {
cell[i][j] = 1;
} else {
cell[i][j] = cell[i - 1][j - 1] + 1; // 公共子串長度
}
} else {
cell[i][j] = 0;
}
if (cell[i][j] > maxLen) {
maxLen = cell[i][j];
maxEnd = j;
}
}
}
return s.substring(maxEnd + 1 - maxLen, maxEnd + 1);
}
}
畫解 1
1-1.png
2-1.png
3-1.png
代碼 2:最長回文子串
class Solution {
public String longestPalindrome(String s) {
if (s.equals("")) {
return "";
}
int length = s.length();
String reversal = new StringBuffer(s).reverse().toString(); // 反轉字符串
int[][] cell = new int[length][length];
int maxLen = 0; // 最長回文子串長度
int maxEnd = 0; // 最長回文子串結束位置
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (reversal.charAt(i) == s.charAt(j)) {
if (i == 0 || j == 0) {
cell[i][j] = 1;
} else {
cell[i][j] = cell[i - 1][j - 1] + 1;
}
}
/**************修改的地方***************************/
// 可為空,不用置為0,減少運行時間
// else {
// cell[i][j] = 0;
// }
/**************************************************/
if (cell[i][j] > maxLen) {
/**************修改的地方***************************/
int beforeIndex = length - 1 - i; // 反向子串末尾字符的原始索引
int firstIndex = j - cell[i][j] + 1; // 子串的首字符索引
if (beforeIndex == firstIndex) {
maxLen = cell[i][j];
maxEnd = j;
}
/**************************************************/
}
}
}
return s.substring(maxEnd + 1 - maxLen, maxEnd + 1);
}
}
畫解 2
1-2.png
2-2(2).png
1.png
2.png
測試用例
描述 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
輸入 | "" | "a" | "abac" | "abcda" | "aacdecaa" |
輸出 | "" | "a" | "aba" | "a" | "aa" |