最長迴文子串

【題目】

給定一個字符串 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可以很好的控制邊界,保證邊界不越界
  • 第二個需要注意的地方是,動態規劃,階段性不僅僅限制於一層的變化,需要考慮多層次的狀態轉移。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章