日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

943. Find the Shortest Superstring

發(fā)布時(shí)間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 943. Find the Shortest Superstring 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 題目描述
  • 暴力搜索分析
  • 暴力搜索優(yōu)化
  • 動(dòng)態(tài)規(guī)劃
  • 參考鏈接

題目描述

輸入:字符串?dāng)?shù)組String[] A
輸出:一個(gè)字符串result,A中每一個(gè)字符串是result的子串,并且reuslt是符合這個(gè)條件的最短的字符串。
舉例:
輸入: [“alex”,“l(fā)oves”,“l(fā)eetcode”]
輸出: “alexlovesleetcode”

輸入: [“catg”,“ctaagt”,“gcta”,“ttca”,“atgcatc”]
輸出: “gctaagttcatgcatc”

暴力搜索分析

分析:result中包含所有A中的字符串,那把A中字符串一次拼接起來(lái)肯定滿足這個(gè)條件。A= [“catg”,“ctaagt”,“gcta”],那么"catgctaagtgcta"符合條件。當(dāng)然這三個(gè)字符串的任意一個(gè)排列得到的字符串都符合。

分析條件2:要求result是最短的。例如A[0]A[1]拼接在一起,如果A[1]的前綴是A[0]的后綴,那么它們可以共用這部分字符串,result的長(zhǎng)度就會(huì)降低。"gcta"和"ctaagt"拼接的時(shí)候,“cta”就是公共部分,拼接之后可以是“gctaagt”。

根據(jù)這些分析我們寫(xiě)一個(gè)暴力搜索的版本(套用排列的代碼模板)。

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遞歸調(diào)用* @param m* 還需要取幾個(gè)元素* @param visited* 哪些元素已經(jīng)被取了,不能再取* @param path* 按順序訪問(wèn)的元素下標(biāo)*/private void dfs(int m,boolean[] visited, ArrayList<Integer> path) {if(m == 0){//注意結(jié)果需要完全拷貝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;}}}/***把路徑中的字符串拼接起來(lái)*/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;}/***拼接兩個(gè)字符串*/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;}}

時(shí)間復(fù)雜度O(n!)。
A的長(zhǎng)度范圍是[1,12]。這個(gè)時(shí)間復(fù)雜度是不能通過(guò)的(花花醬視頻中的說(shuō)明)。12!約等于4億多??梢钥紤]剪枝策略和緩存策略。

暴力搜索優(yōu)化

從遞歸樹(shù)中我們可以看到相同位置的字符串拼接會(huì)有多次操作。例如路徑[1,2,3]、[2,3,1]這兩個(gè),A[2]和A[3]就要拼接兩次。是不是能提前計(jì)算出兩兩字符串拼接后的字符串,可以少一次。
我們要找的是長(zhǎng)度最短的字符串,如果能提前把兩兩字符串拼接后的字符串的長(zhǎng)度記錄下來(lái),在dfs過(guò)程中發(fā)現(xiàn)當(dāng)前長(zhǎng)度大于result(上一個(gè)最有結(jié)果)的長(zhǎng)度就可以停止遞歸。這樣我們需要計(jì)算一個(gè)數(shù)組cost[i][j],表示A[j]拼接在A[i]后面需要增加的長(zhǎng)度。
例如 A= [“catg”,“ctaagt”,“gcta”], cost[0][0]=0。cost[0][2]=3,因?yàn)楹喜⒑蟮淖址甤atgcta,需要增加cta三個(gè)字符串。
代碼鏈接。

動(dòng)態(tài)規(guī)劃

我們的目標(biāo)是要將A中每一個(gè)字符串添加到result中。在實(shí)際操作過(guò)程中,每次添加一個(gè)字符串,并且前面的字符串怎么添加不影響后續(xù)字符串添加??梢允褂脛?dòng)態(tài)規(guī)劃。
定義int s 表示訪問(wèn)了哪些節(jié)點(diǎn)。例如s=3,表示訪問(wèn)了A[0],A[1]。對(duì)于A= [“catg”,“ctaagt”,“gcta”],s最大值等于7。

定義數(shù)組dp[s][i] = 經(jīng)過(guò)路徑s,到達(dá)i狀態(tài),并且是以i結(jié)尾,并且每個(gè)節(jié)點(diǎn)只訪問(wèn)一次的最短字符串長(zhǎng)度。目標(biāo)狀態(tài)是dp[7][i],從dp[7][0]、dp[7][1]、dp[7][2]中選擇最小值作為結(jié)果。

這里動(dòng)態(tài)方程,不太好表示??梢允褂脧南孪蛏系姆绞健?br /> 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]}

初始化,添加每個(gè)單個(gè)的字符串到結(jié)果中。dp[0][0] = A[0].length(),dp[1][1]=A[1].length(),dp[4][2]=A[2].length()

時(shí)間復(fù)雜度(2^n)。時(shí)間復(fù)雜度降低很多。這個(gè)代碼有很多難點(diǎn)。即使會(huì)了遞歸方程,要想實(shí)現(xiàn)出來(lái)還是有難度的。
難點(diǎn)1,用int 表示數(shù)組中每一位是否被選擇 。
難點(diǎn)2,動(dòng)態(tài)規(guī)劃從s=1開(kāi)始,逐步遞增。
難點(diǎn)3,如果題目求最短字符串的長(zhǎng)度的話,只要使用一維數(shù)組dp[]即可,這里還要請(qǐng)求輸出字符串,所以需要記錄下走不通路徑到達(dá)i狀態(tài)的長(zhǎng)度。
難點(diǎn)4,記錄路徑需要用到parent數(shù)組。

class Solution {public String shortestSuperstring(String[] A) {int n = A.length;int[][] cost = new int[n][n];for(int i=0;i<n;i++){for(int j = 0; j<n;j++){cost[i][j] = minLength(A[i], A[j]);}}int[][] dp = new int[1<<n][n];int[][] parent = new int[1<<n][n];for(int s = 0; s < (1<<n); s++){Arrays.fill(dp[s],10000);Arrays.fill(parent[s],-1);}for(int i=0;i<n;i++){dp[1<<i][i] = A[i].length();}for(int s = 1; s < (1<<n); s++){ for(int j = 0;j<n;j++){//end pointif ((s & (1 << j)) ==0) continue;int pre = s - (1<<j);for(int i =0;i<n;i++){if(dp[pre][i] + cost[i][j] < dp[s][j]){dp[s][j] = dp[pre][i] + cost[i][j];parent[s][j] = i;}}}}int mask = (1<<n)-1;int minCost = dp[mask][0];int endIndex = 0;for(int j =1;j<n;j++){if(dp[mask][j] < minCost){minCost = dp[mask][j] ;endIndex = j;}}String result = "";int cur = endIndex;int s = (1<<n)-1;while(s > 0){int p = parent[s][cur];if(p<0){result = A[cur] + result;break;}else{result = A[cur].substring(A[cur].length()-cost[p][cur]) + result;}s &= ~(1 << cur);cur = p;}return result;}private int minLength(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 str2.length()-i;}}return str2.length();}}

參考鏈接

花花醬,
leetcode

總結(jié)

以上是生活随笔為你收集整理的943. Find the Shortest Superstring的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。