943. Find the Shortest Superstring
目錄
- 題目描述
- 暴力搜索分析
- 暴力搜索優化
- 動態規劃
- 參考鏈接
題目描述
輸入:字符串數組String[] A
輸出:一個字符串result,A中每一個字符串是result的子串,并且reuslt是符合這個條件的最短的字符串。
舉例:
輸入: [“alex”,“loves”,“leetcode”]
輸出: “alexlovesleetcode”
輸入: [“catg”,“ctaagt”,“gcta”,“ttca”,“atgcatc”]
輸出: “gctaagttcatgcatc”
暴力搜索分析
分析:result中包含所有A中的字符串,那把A中字符串一次拼接起來肯定滿足這個條件。A= [“catg”,“ctaagt”,“gcta”],那么"catgctaagtgcta"符合條件。當然這三個字符串的任意一個排列得到的字符串都符合。
分析條件2:要求result是最短的。例如A[0]A[1]拼接在一起,如果A[1]的前綴是A[0]的后綴,那么它們可以共用這部分字符串,result的長度就會降低。"gcta"和"ctaagt"拼接的時候,“cta”就是公共部分,拼接之后可以是“gctaagt”。
根據這些分析我們寫一個暴力搜索的版本(套用排列的代碼模板)。
class Solution {private String result = null;private String[] A;public String shortestSuperstring(String[] A) {result = null;this.A = A;boolean[] visited = new boolean[A.length];dfs(A.length,visited,new ArrayList<Integer>());return result;}/*** dfs遞歸調用* @param m* 還需要取幾個元素* @param visited* 哪些元素已經被取了,不能再取* @param path* 按順序訪問的元素下標*/private void dfs(int m,boolean[] visited, ArrayList<Integer> path) {if(m == 0){//注意結果需要完全拷貝String str = contanctString(path);if(result == null || result.length() > str.length()){result = str;}return;}for(int i =0;i<A.length;i++){if(visited[i]==false){visited[i] = true;path.add(i);dfs(m-1,visited,path);path.remove(path.size()-1);visited[i] = false;}}}/***把路徑中的字符串拼接起來*/private String contanctString(List<Integer> path){String str = A[path.get(0)];for(int i = 1; i< path.size();i++){str = contanctTwoString(str, A[path.get(i)]);}return str;}/***拼接兩個字符串*/private String contanctTwoString(String str1 , String str2){int m = Math.min(str1.length(),str2.length());for(int i = m; i>0;i--){if(str1.endsWith(str2.substring(0,i))){return str1+str2.substring(i);}}return str1+str2;}}時間復雜度O(n!)。
A的長度范圍是[1,12]。這個時間復雜度是不能通過的(花花醬視頻中的說明)。12!約等于4億多。可以考慮剪枝策略和緩存策略。
暴力搜索優化
從遞歸樹中我們可以看到相同位置的字符串拼接會有多次操作。例如路徑[1,2,3]、[2,3,1]這兩個,A[2]和A[3]就要拼接兩次。是不是能提前計算出兩兩字符串拼接后的字符串,可以少一次。
我們要找的是長度最短的字符串,如果能提前把兩兩字符串拼接后的字符串的長度記錄下來,在dfs過程中發現當前長度大于result(上一個最有結果)的長度就可以停止遞歸。這樣我們需要計算一個數組cost[i][j],表示A[j]拼接在A[i]后面需要增加的長度。
例如 A= [“catg”,“ctaagt”,“gcta”], cost[0][0]=0。cost[0][2]=3,因為合并后的字符串catgcta,需要增加cta三個字符串。
代碼鏈接。
動態規劃
我們的目標是要將A中每一個字符串添加到result中。在實際操作過程中,每次添加一個字符串,并且前面的字符串怎么添加不影響后續字符串添加。可以使用動態規劃。
定義int s 表示訪問了哪些節點。例如s=3,表示訪問了A[0],A[1]。對于A= [“catg”,“ctaagt”,“gcta”],s最大值等于7。
定義數組dp[s][i] = 經過路徑s,到達i狀態,并且是以i結尾,并且每個節點只訪問一次的最短字符串長度。目標狀態是dp[7][i],從dp[7][0]、dp[7][1]、dp[7][2]中選擇最小值作為結果。
這里動態方程,不太好表示。可以使用從下向上的方式。
dp[7][0] = min(dp[6][2] + cost[2][0]
, dp[6][1] + cost[1][0]
, dp[5][0] + cost[0][1]
…)
dp[mask ^ (1<<j)][j] = min{dp[mask][i] + cost[i][j]}
初始化,添加每個單個的字符串到結果中。dp[0][0] = A[0].length(),dp[1][1]=A[1].length(),dp[4][2]=A[2].length()
時間復雜度(2^n)。時間復雜度降低很多。這個代碼有很多難點。即使會了遞歸方程,要想實現出來還是有難度的。
難點1,用int 表示數組中每一位是否被選擇 。
難點2,動態規劃從s=1開始,逐步遞增。
難點3,如果題目求最短字符串的長度的話,只要使用一維數組dp[]即可,這里還要請求輸出字符串,所以需要記錄下走不通路徑到達i狀態的長度。
難點4,記錄路徑需要用到parent數組。
參考鏈接
花花醬,
leetcode
總結
以上是生活随笔為你收集整理的943. Find the Shortest Superstring的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MOSSE目标跟踪算法步骤
- 下一篇: LoadRunner 12.02 安装教