leetcode131. 分割回文串 Java (看得懂的詳細分解)

題目:
給定一個字符串 s,將 s 分割成一些子串,使每個子串都是迴文串。

返回 s 所有可能的分割方案。

示例:

輸入: "aab"
輸出:
[
  ["aa","b"],
  ["a","a","b"]
]

對於要求列出所有情況的,我們通常直接考慮使用回溯算法。需要在回溯的過程中加上一些小的技巧,例如通過條件排除一些情況等,來優化算法的時間複雜度。

對於本題,我們嘗試這樣思考:要得到所有都是迴文串的分割方案,我們就需要對字符串進行分割,至於需要分割幾下,我們不知道。那麼首先第一次分割的地方可以是第一個字符串之後,也可以是第二第三。。。個字符串之後,第二次分割是從上次分割之後的字符串開始,同樣長度可以是一二三。。。個字符串。這樣就能夠得出我們分割幾下取決於什麼時候碰到最後邊界。

這樣一想,這就是經典的回溯算法,最後的結果需要若干步驟才能得到,每一步又有很多個選擇,如果深入進去發現這條路不是最後的結果,那就退回來繼續嘗試其他的路徑。

解決本題的時候有一個關鍵技巧,就是在判斷子串是否是迴文串的時候,保存之前判斷的情況,這樣就能夠優化時間複雜度。

下面的代碼時間複雜度超過了100%的提交,代碼中添加了詳細註釋,結合上面的應該可以充分理解。

import java.util.ArrayList;
import java.util.List;

/**
 * 回溯算法
 * 時間複雜度:3ms,100%
 * 空間複雜度:38.6MB,97.10%
 */
public class Solution_131 {
    /*
    用來保存從任意一位置至任意一位置的子串是否是迴文串,類似於動態規劃中保存之前的狀態來減小時間複雜度
    不同之處在於這裏的狀態並沒有發生轉移,所以不算是動態規劃與回溯算法的結合
    這一步是優化時間複雜度的關鍵
     */
    int[][] dp;

    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<>();
        if (null == s || s.length() == 0) {
            return res;
        }
        int length = s.length();
        /*
        它是一個二維矩陣,有三種狀態值:
        0表示之前還沒有計算過
        1表示從下表i到j的子串是迴文串
        2表示不是迴文串
        我們只用到了數組的右上半部分
        當然這裏也可以使用char數組,空間複雜度更低
         */
        dp = new int[length][length];
        //初始化,單個字符的肯定是迴文串
        for (int i = 0; i < length; i++) {
            dp[i][i] = 1;
        }

        ArrayList<String> templist = new ArrayList<>();
        helper(res, templist, s, length, 0);
        return res;
    }

    /**
     * 回溯算法
     *
     * @param res      結果集
     * @param templist 中間list
     * @param s        字符串
     * @param length   字符串長度
     * @param index    從當前位置向後組合判斷
     */
    private void helper(List<List<String>> res, ArrayList<String> templist, String s, int length, int index) {
    	//走到這一步就表示走到了最後,添加到結果集
        if (index == length) {
            res.add(new ArrayList<>(templist));//一定要重新new一個對象,templist可以得到重用
        }
        //走到某一步有若干的選擇繼續走下一步
        for (int i = index; i < length; i++) {
            if (isPalindrome(s, index, i)) {
                templist.add(s.substring(index, i + 1));
                helper(res, templist, s, length, i + 1);
                templist.remove(templist.size() - 1);//回溯算法中回退一定要記得這一步
            }
        }
    }

    //判斷是否是迴文串,這裏首先需要到保存的狀態中查看是否之前已經有了,優化時間複雜度
    private boolean isPalindrome(String s, int from, int to) {
        if (dp[from][to] == 1) {
            return true;
        } else if (dp[from][to] == 2) {
            return false;
        } else {
            for (int i = from, j = to; i < j; i++, j--) {
                if (s.charAt(i) != s.charAt(j)) {
                    dp[from][to] = 2;
                    return false;
                }
            }
            dp[from][to] = 1;
            return true;
        }
    }

/*    public static void main(String[] args) {
        List<List<String>> res = new Solution_131().partition("aab");
        System.out.println(res);
    }*/
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章