943.Find the Shortest Superstring --- 旅行商問題&狀態壓縮DP

943.Find the Shortest Superstring

Given an array A of strings, find any smallest string that contains each string in A as a substring.java

We may assume that no string in A is substring of another string in A.node

Example 1:算法

Input: ["alex","loves","leetcode"]app

Output: "alexlovesleetcode"ui

Explanation: All permutations of "alex","loves","leetcode" would also be accepted.code

2019.1.19算法羣裏的題目:這道題涉及到旅行商問題,解法是狀態壓縮dpleetcode

參考大神的解法,梳理了下解法;字符串

題意

給一個字符串序列,找到包含序列中全部字符串的最小的字符串get

分析

  • (1)先建圖:預處理計算出Cost,也就是一個節點到另外一個節點的權重,這裏的話,,g[i][j]表示將A[j]加到A[i]的後面,路徑的長度:string

  • (2) 題目轉化爲找一條訪問過每一個節點一次的最短的路徑

  • (3) 記憶化DP:
    • dp[s][i] :表示訪問了節點集合s,且最後一個節點是i的最短的路徑,注意這裏s是一個Binary String,表示哪些節點已經訪問過了,爲1的節點是已經訪問過的;

      Assume we want to travel nodes: {n1, n2, n3, n4} then
      i = 2 ^ n1 +2 ^ n2 +2 ^ n3 +2 ^ n4;

    • path[s][i]:表示訪問了節點集合s,而且最後一個節點是i,i前面的一個節點; 記錄路徑中,i的前面的一個點,以便於重建途徑的時候的回溯

  • (4) dp更新策略:
    dp[s][i] = min(dp[s - 2^i][j] + g[j][i]) #將j加到i的後面

代碼

/**
題意:給一個字符串序列,找到包含序列中全部字符串的最小的子串
分析:
*/

class Solution {
    public String shortestSuperstring(String[] A) {
        int n = A.length;
        int[][] graph = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                graph[i][j] = calCost(A, i, j);  //把j加到i後面的路徑長度,即圖中i->j的邊長
                graph[j][i] = calCost(A, j, i);
            }
        }
        
        int[][] dp = new int[1 << n][n];  //dp[s][i] :表示訪問了節點集合s,且最後一個節點是i的最短的路徑
        int[][] path = new int[1 << n][n];
        int min = Integer.MAX_VALUE, last = -1;
        
        for (int state = 1; state < (1 << n); state++) {  //枚舉全部的節點集合組成狀態
            Arrays.fill(dp[state], Integer.MAX_VALUE);
            for (int node = 0; node < n; node++) {
                if ((state & (1 << node)) > 0) { //判斷node在不在節點集合中
                    int leftState = state - (1 << node);  //剩下的節點集合
                    if (leftState == 0) {  //若是隻剩一個節點了,則當前長度就是node的長度
                        dp[state][node] = A[node].length();
                    } else {
                        for (int k = 0; k < n; k++) {  //dp更新
                            if (dp[leftState][k] != Integer.MAX_VALUE && 
                                dp[leftState][k] + graph[k][node] < dp[state][node]) {  //若是訪問過了leftState且通過k點的路徑更小,則更
                                dp[state][node] = dp[leftState][k] + graph[k][node];
                                path[state][node] = k;
                            }
                        }
                    }
                }
                if (state == (1 << n) - 1 && dp[state][node] < min) {
                    min = dp[state][node];
                    last = node;
                }
                //System.out.println(dp[state][node]);
            }
        }
        //創建路徑        
        StringBuilder sb = new StringBuilder();
        int cur = (1 << n) - 1;
        Stack<Integer> stack = new Stack<>();
        while (cur > 0) {
            stack.push(last);
            int temp = cur;
            cur -= (1 << last);
            last = path[temp][last];
        }
                
        int i = stack.pop();
        sb.append(A[i]);
        while (!stack.isEmpty()) {
            int j = stack.pop();
            sb.append(A[j].substring(A[j].length() - graph[i][j]));
            i = j;
        }
        return sb.toString();
    }
    
    private int calCost(String[] A, int i, int j) {
        for (int pos = 1; pos < A[i].length(); pos++) {
            if (A[j].startsWith(A[i].substring(pos))) {
                return A[j].length() - A[i].length() + pos;
            }
        }
        return A[j].length();
    }
}
相關文章
相關標籤/搜索