Skip to content

Latest commit

 

History

History
executable file
·
203 lines (188 loc) · 6.4 KB

943. Find the Shortest Superstring.md

File metadata and controls

executable file
·
203 lines (188 loc) · 6.4 KB

943. Find the Shortest Superstring

Question

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

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

Example 1:

Input: ["alex","loves","leetcode"]
Output: "alexlovesleetcode"
Explanation: All permutations of "alex","loves","leetcode" would also be accepted.

Example 2:

Input: ["catg","ctaagt","gcta","ttca","atgcatc"]
Output: "gctaagttcatgcatc"

Note:

  • 1 <= A.length <= 12
  • 1 <= A[i].length <= 20

Solution:

  • Method 1: search O(N! + N^2) AC 775ms
    • First create a cost[i][j] matrix, means the number of characters need to be added at the end of the result if last added word is A[i] and we want to add A[j]. Cost matrix is calculated at the very beginning so it will be calculated once, and its time efficiency is O(n^2).
    • We list all possible permutations and save the minimum. Pruning is required for remove useless iterations.
    • We only record the path of words it goes through and concat the string at the end(we only concat once).
    class Solution {
        public String shortestSuperstring(String[] A) {
            if(A.length == 1) return A[0];
            int[][] cost = new int[A.length][A.length];
            for(int i = 0; i < A.length; i++){
                LABEL:
                for(int j = 0; j < A.length; j++){
                    if(i == j){
                        cost[i][j] = A[j].length();
                    }else{
                        int start = A[i].indexOf(A[j].charAt(0));
                        if(start == -1) cost[i][j] = A[j].length();
                        else{
                            for(int k = start; k < A[i].length(); k++){
                                if(A[j].startsWith(A[i].substring(k))){
                                    cost[i][j] = A[j].length() - (A[i].length() - k);
                                    continue LABEL;
                                }
                            }
                            cost[i][j] = A[j].length();
                        }
                    }
                }
            }
            List<Integer> res = new ArrayList<>();
            dfs(A, cost, 0, 0, new ArrayList<Integer>(), res, 0);
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < res.size(); i++){
                Integer path = res.get(i);
                sb.append(i == 0 ? A[path]: A[path].substring(A[path].length() - cost[res.get(i - 1)][path]));
            }
            return sb.toString();
        }
        private int sum = Integer.MAX_VALUE;
        private void dfs(String[] A, int[][] cost, int index, int temp, List<Integer> path, List<Integer> res, int visited){
            if(temp >= sum) return;
            if(A.length == index){
                sum = temp;
                res.clear();
                res.addAll(path);
                return;
            }
            for(int i = 0; i < A.length; i++){
                if((visited & (1 << i)) > 0) continue;
                path.add(i);
                dfs(A,
                    cost,
                    index + 1,
                    index == 0 ? A[i].length(): temp + cost[path.get(index - 1)][i],
                    path,
                    res,
                    (visited | (1 << i)));
                path.remove(index);
            }
        }
    }

C++ Version

  • Method 1: brutal force

     class Solution {
     public:
     	string shortestSuperstring(vector<string>& A) {
     		const int n = A.size();
     		g_ = vector<vector<int>>(n, vector<int>(n));
     		for(int f = 0; f < n; ++f){
     			int flen = A[f].length();
     			for(int s = 0; s < n; ++s){
     				if(s == f) continue;
     				int slen = A[s].length();
     				g_[f][s] = slen;
     				for(int i = 1; i <= min(flen, slen); ++i){
     					if(A[f].substr(flen - i) == A[s].substr(0, i))
     						g_[f][s] = slen - i;
     				}
     			}
     		}
     		vector<int> path(n);
     		best_len_ = INT_MAX;
     		dfs(A, 0, 0, 0, path);
     		string res = A[path_[0]];
     		for(int i = 1; i < n; ++i){
     			int cur_len = A[path_[i]].length();
     			res += A[path_[i]].substr(cur_len - g_[path_[i - 1]][path_[i]]);
     		}
     		return res;
     	}
     private:
     	vector<vector<int>> g_;
     	vector<int> path_;
     	int best_len_;
     	
     	void dfs(const vector<string>& A, int d, int cur_len, int used, vector<int>& path){
     		if(cur_len >= best_len_) return;
     		if(d == A.size()){
     			best_len_ = cur_len;
     			path_ = path;
     			return;
     		}
     		for(int i = 0; i < A.size(); ++i){
     			if(used & (1 << i)) continue;
     			path[d] = i;
     			dfs(A, 
     				d + 1,
     				d == 0 ? A[i].length() : cur_len + g_[path[d - 1]][i],
     				used | (1 << i),
     				path);
     		}
     	}
     };
  • Method 2: DP

    1. dp[s][j]: s is a visited set in binary form, j is the output of current state.
    2. Transition function: dp[s][j] = min(dp[s - (1 << j)][i] + g[i][j])
    3. initialization: dp[1 << i][i] = A[i].length(), current string's length.
    4. final result: min(dp[2^n - 1][*]) with traceback.
     class Solution {
     public:
     	string shortestSuperstring(vector<string>& A) {
     		const int n = A.size();
     		vector<vector<int>> g(n, vector<int>(n));
     		for(int f = 0; f < n; ++f){
     			int flen = A[f].length();
     			for(int s = 0; s < n; ++s){
     				if(s == f) continue;
     				int slen = A[s].length();
     				g[f][s] = slen;
     				for(int i = 1; i <= min(flen, slen); ++i){
     					if(A[f].substr(flen - i) == A[s].substr(0, i))
     						g[f][s] = slen - i;
     				}
     			}
     		}
     		vector<vector<int>> dp(1 << n, vector<int>(n, INT_MAX >> 1));
     		vector<vector<int>> parent(1 << n, vector<int>(n, -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){ // 最后的出口结点
     				if(!(s & (1 << j))) continue;
     				int ps = s & ~(1 << j);
     				for(int i = 0; i < n; ++i){
     					if(!(ps & (1 << i))) continue;
     					if(dp[ps][i] + g[i][j] < dp[s][j]){
     						dp[s][j] = dp[ps][i] + g[i][j];
     						parent[s][j] = i;
     					}
     				}
     			}
     		}
     		
     		auto it = min_element(begin(dp.back()), end(dp.back()));
     		int j = it - begin(dp.back());
     		int s = (1 << n) - 1;
     		string res;
     		while(s){
     			int i = parent[s][j];
     			if(i < 0) res = A[j] + res;
     			else    res = A[j].substr(A[j].length() - g[i][j]) + res;
     			s &= ~(1 << j);
     			j = i;
     		}
     		return res;
     	}
     };

Reference

  1. 花花酱 LeetCode 943. Find the Shortest Superstring - 刷题找工作 EP231