問題描述https://leetcode-cn.com/problems/longest-palindromic-substring/
動態規劃的四個過程
1.劃分狀態,即劃分子問題。
如果一個字符串是迴文字符串,那麼它肯定滿足1.兩端的兩個字符相等 2.除去兩端兩個字符剩下的還是迴文字符串。以此往字符串串中心位置類推都滿足這個規律。那可以找任意一個字符作爲中心點,從中心點開始向兩邊擴散,判斷是否滿足迴文。
2.狀態表示,即如何讓計算機理解子問題。
子字符串的起始位置和截止位置,可以類比到二維數組的橫縱座標,即用arrs[start][end]表示str.substring(start, end+1)子串,數組的值設置爲boolean,表示這一段是不是迴文字符串。這樣自底向上計算的時候,就可以利用已經計算過的狀態。
3.狀態轉移,即父問題是如何由子問題推導出來的。
根據1的劃分狀態,一個字符串是不是迴文,可以表示爲
arrs[start][end] = arrs[start+1][end-1] && str.charAt(start)==str.charAt(end)
4.確定邊界。
- 如果字符串長度爲1,默認就是迴文
- 如果字符串長度爲2,滿足兩個字符相等是迴文
- 如果字符串長度爲3,滿足第一個和第三個字符相等是迴文,第二個作爲中心是什麼無所謂了
- 如果字符串長度>3,滿足除最兩端兩個字符外的內部字符是迴文,並且第一個和最後一個字符相等
代碼
以l表示子串的起始位置,r表示子串的截止位置,把所有子串遍歷一遍,判讀是不是迴文,並以一個變量記錄最長迴文的長度,如果後續有更長的迴文時,更新這個值。寫出來的代碼如下所示
private static void subStr() {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
int length = s.length();
// 邊界:如果字符串長度爲1,默認就是迴文
if (s.length() == 1) {
System.out.println("longest :" + s);
return;
}
// start、end記錄迴文子串的起止位置,manLen記錄迴文的最大長度
int start = 0;
int end = 0;
int maxLen = 0;
// 二維數組把自底向上的判斷結果暫存,用於狀態轉移方程的推導
boolean[][] arrs = new boolean[length][length];
// 結尾的位置從1到最後一位
for (int r = 1; r < length; r++) {
// 起始的位置從0到結尾位置之前(< r)
for (int l = 0; l < r; l++) {
// 長度爲2,中心位置是兩個相同的字符
if (r - l == 1 && s.charAt(l) == s.charAt(r)) {
arrs[l][r] = true;
if (r - l + 1 > maxLen) {
start = l;
end = r;
maxLen = (r - l) * 2;
}
// 長度爲3,中心位置是一個字符,中心兩側的兩個字符相等
} else if (r - l == 2 && s.charAt(l) == s.charAt(r)) {
arrs[l][r] = true;
if (r - l + 1 > maxLen) {
start = l;
end = r;
maxLen = (r - l) * 2;
}
//長度超過3,滿足狀態轉移方程
} else if (arrs[l+1][r-1] && s.charAt(l) == s.charAt(r)) {
arrs[l][r] = true;
if (r - l + 1 > maxLen) {
start = l;
end = r;
maxLen = (r - l) * 2;
}
}
}
}
System.out.println("longest :" + s.substring(start, end + 1)); // substring不包括endIndex,end + 1
}
三種分支判斷合併以後
private static void subStr2() {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
int length = s.length();
// 邊界:如果字符串長度爲1,默認就是迴文
if (s.length() == 1) {
System.out.println("longest :" + s);
return;
}
// start、end記錄迴文子串的起止位置,manLen記錄迴文的最大長度
int start = 0;
int end = 0;
int maxLen = 0;
// 二維數組把自底向上的判斷結果暫存,用於狀態轉移方程的推導
boolean[][] arrs = new boolean[length][length];
for (int r = 1; r < length; r++) { // 結尾的位置從1到最後一位
for (int l = 0; l < r; l++) { // 起始的位置從0到結尾位置之前(< r)
// ||的優先級低於&&,前半段加括號
if ((arrs[l+1][r-1] || r - l <= 2) && s.charAt(l) == s.charAt(r)) {
arrs[l][r] = true;
if (r - l + 1 > maxLen) {
start = l;
end = r;
maxLen = r - l + 1;
}
}
}
}
// substring不包括endIndex,end + 1
System.out.println("longest :" + s.substring(start, end + 1));
}