動態規劃+中心擴展重新理解尋找最長迴文子串

問題描述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));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章