這是悅樂書的第342次更新,第366篇原創
01 看題和準備
今天介紹的是LeetCode算法題中Medium級別的第3題(順位題號是5)。給定一個字符串s,找到s中最長的迴文子字符串。 您可以假設s的最大長度爲1000。例如:
輸入:“babad”
輸出:“bab”
注意:“aba”也是一個有效的答案。
輸入:“cbbd”
輸出:“bb”
02 第一種解法
暴力解法。
使用兩層循環截取出所有的子串,判斷該子串是否是迴文,從中取長度最長的子串作爲結果輸出。
此解法時間複雜度是O(N^3)
,空間複雜度是O(1)
。
public String longestPalindrome(String s) {
int max = 0, n = s.length();
String result = "";
for (int i=0; i<n; i++) {
for (int j=i+1; j<=n; j++) {
String tem = s.substring(i,j);
if (isPalindrome(tem)) {
if (j-i > max) {
max = j-i;
result = tem;
}
}
}
}
return result;
}
public boolean isPalindrome(String s){
int left = 0, right = s.length()-1;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
03 第二種解法
我們也可以換一種找回文的方式,從左右兩邊向中間變成由中間向左右兩邊。
此時需要考慮迴文的長度是奇數還是偶數的情況,如果是奇數形迴文,就以當前字符爲中心左右兩邊尋找,例如迴文"bab";如果是偶數形迴文,需要兩個字符,並且這兩個字符是相等的,則需要以當前字符和其相鄰的字符爲中心向左右兩邊尋找,例如迴文"abba"。
此解法的時間複雜度是O(N^2)
,空間複雜度是O(1)
。
public String longestPalindrome2(String s) {
if (s.length() < 2) {
return s;
}
int n = s.length(), start = 0, end = 0;
for (int i=0; i<n-1; i++) {
int len = helper(s, i, i);
int len2 = helper(s, i, i+1);
int len3 = Math.max(len, len2);
if (len3 > end - start) {
start = i - (len3-1)/2;
end = i + len3/2;
}
}
return s.substring(start, end+1);
}
/**
* 以當前字符爲中心向左右兩邊擴散,尋找回文子串
* @param s 字符串
* @param left 起始索引
* @param right 結束索引
* @return 迴文子串長度
*/
public int helper(String s, int left, int right) {
int n = s.length(), L = left, R = right;
while (L >= 0 && R < n && s.charAt(L) == s.charAt(R)) {
// 繼續向左尋找
L--;
// 繼續向右尋找
R++;
}
return R - L -1;
}
04 第三種解法
動態規劃算法,用空間換時間,是對第一種解法的改進。
此解法的時間複雜度是O(N^2)
,空間複雜度是O(N^2)
。
public String longestPalindrome3(String s) {
if (s.length() < 2) {
return s;
}
int n = s.length(), start = 0, end = 0;
int maxLen = 0;
// dp[j][i]表示子串[j,i]是迴文
boolean[][] dp = new boolean[n][n];
// 右邊界
for (int i=0; i<n; i++) {
// 左邊界
for (int j=i; j>=0; j--) {
if (i == j) {
dp[j][i] = true;
} else if (s.charAt(i) == s.charAt(j)) {
// 迴文中至少3個字符
if (j < i-1) {
dp[j][i] = dp[j+1][i-1];
} else {
dp[j][i] = true;
}
} else {
dp[i][j] = false;
}
// 比較最大值,並重新賦值
if (i-j+1 > maxLen && dp[j][i]) {
maxLen = i-j+1;
start = j;
end = i;
}
}
}
return s.substring(start, end+1);
}
05 第四種解法
馬拉車算法(Manacher's Algorithm),來自於討論區,這是第一次聽說這種算法,將時間複雜度降低到了O(N)
,也是很厲害了,後續抽時間來詳細瞭解下這個算法。
public String longestPalindrome4(String s) {
String T = preProcess(s);
int n = T.length();
int[] P = new int[n];
int C = 0, R = 0;
for (int i = 1; i < n - 1; i++) {
int i_mirror = 2 * C - i;
if (R > i) {
P[i] = Math.min(R - i, P[i_mirror]);
} else {
P[i] = 0;
}
while (T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i])) {
P[i]++;
}
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
}
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n - 1; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
int start = (centerIndex - maxLen) / 2;
return s.substring(start, start + maxLen);
}
/**
*
* @param s
* @return
*/
public String preProcess(String s) {
int n = s.length();
if (n == 0) {
return "^$";
}
String ret = "^";
for (int i = 0; i < n; i++) {
ret += "#" + s.charAt(i);
}
ret += "#$";
return ret;
}
06 小結
算法專題目前已連續日更超過六個月,算法題文章211+篇,公衆號對話框回覆【數據結構與算法】、【算法】、【數據結構】中的任一關鍵詞,獲取系列文章合集。
以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支持!