【題目】
給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲 1000。
輸入: “babad”
輸出: “bab”
注意: “aba” 也是一個有效答案。
解法一、暴力法
很明顯,暴力法將選出所有子字符串可能的開始和結束位置,並檢驗它是不是迴文。
- 時間複雜度:O(n^3)
- 空間複雜度:O(1)
【代碼】
public String longestPalindrome1(String s) {
if (s==null || s.length()==0) return "";
int i,j,l,left,right,temp,resLeft,resRight;
l = 1;
resLeft = resRight =0;
for (i=0; i<s.length();i++) {
for (j=i+1;j<s.length();j++) {
left = i;
right = j;
while (left<right) {
if (s.charAt(left) == s.charAt(right)) {
left ++;
right --;
} else {
break;
}
}
if (left < right) {
temp = 0;
} else {
temp = j - i + 1;
}
if (l < temp) {
l = temp;
resLeft = i;
resRight = j;
}
}
}
return s.substring(resLeft,resRight+1);
}
解法二、動態規劃
/**
* 動態規劃進階:二維數組和公式
* dp[i][j] 表示 i到j是迴文子串
* dp[i+1][j+1]是迴文子串的條件:dp[i][j]是迴文並且s(i+1)==s(j+1)
* dp[i][i+1]是迴文,只需要,s(i)==s(i+1)
* 邊界:所有的單字符以及二回文子串
* dp[i][i] 全部是迴文子串
* 判斷dp[i][i+1]是不是迴文,條件:s(i)==s(i+1)
* */
這產生了一個直觀的動態規劃解法,我們首先初始化一字母和二字母的迴文,然後找到所有三字母迴文,並依此類推…
- 時間複雜度:O(n^2)
- 空間複雜度:O(n^2)
【代碼】
public String longestPalindrome(String s) {
if (s==null || s.length()==0) return "";
int i,j,len,l,r,res,k;
len = s.length();
boolean[][] dp = new boolean[len][len];
// 初始化邊界,把迴文子串長度1和2的找出來
dp[0][0] = true;
l = r = 0;
res = 1;
for (i=1; i<len; i++) {
dp[i][i] = true;
if (s.charAt(i-1) == s.charAt(i)) {
dp[i-1][i] = true;
res = 2;
l = i-1;
r = i;
}
}
//i表示檢索的子串長度,等於3表示先檢索長度爲3的子串
for (i=3; i<=len; i++) {
for (j=0; (j+i-1)<len; j++) {
k = j+i-1;
if (dp[j+1][k-1] && s.charAt(j)==s.charAt(k)) {
dp[j][k] = true;
if (res < (k-j+1)) {
res = k-j+1;
l = j;
r = k;
}
}
}
}
return s.substring(l,r+1);
}
這裏有幾個需要注意的地方:
- 動態規劃從來都是當前階段i,j 根據之前的狀態(無論是前一狀態還是前幾個狀態)的條件符合情況來判斷是否成立,而不是根據當前狀態去判斷後面的狀態;
- 簡而言之:dp[i][j] = {dp[i-index][j-index]},而不是dp[i+index][j+index] = {dp[x][y]},這是因爲處理當前的dp可以很好的控制邊界,保證邊界不越界
- 第二個需要注意的地方是,動態規劃,階段性不僅僅限制於一層的變化,需要考慮多層次的狀態轉移。