【LeetCode 943】 Find the Shortest Superstring

风流意气都作罢 提交于 2020-03-05 01:15:03

题目描述

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

思路

思路一: DFS。时间复杂度:O(n!) 。注意剪枝:当前路径的长度>已有的最短路径长度时,不用继续搜索。然后,如果在搜索,保存最优字符串,不断的对字符串操作很费时间,可以保存路径,最后根据路径获得最后的字符串。
思路二: 动态规划。dp[s][i] 表示s状态,以i结尾的字符串的最少代价。代价即两个字符串连起来需要增加的字符个数。以二进制位的01状态表示该位置的字符串是否被访问过。所以s状态可以由s-(1<<i),i为s中所有为1的位置,最小值。时间复杂度:O(n^2 * 2^n)

代码

DFS:

class Solution {
public:
    string shortestSuperstring(vector<string>& A) {
        lap = vector<vector<int>> (A.size(), vector<int>(A.size()));
        preprocess(A);
        path = vector<int> (A.size());
        dfs(A, 0, 0, 0);
        ans = A[bestpath[0]];
        for (int i=1; i<bestpath.size(); ++i) {
            int pre = bestpath[i-1];
            int cur = bestpath[i];
            ans += A[cur].substr(lap[pre][cur]);
        }
        return ans;
    }
    
    void dfs(vector<string>& A, int cnt, int curlen, int used) {
        if (cnt == A.size()) {
            if (curlen < bestlen) {
                bestlen = curlen;
                bestpath = path;
            }
            return;
        }
        
        if (curlen >= bestlen) return;
        
        for (int i=0; i<A.size(); ++i) {
            if (used & (1<<i)) continue;
            int len = (cnt == 0 ? 0 : lap[path[cnt-1]][i]);
            path[cnt] = i;
            dfs(A, cnt+1, curlen+A[i].length()-len, used | (1<<i));
        }
        return;
    }
    
private:
    string ans;
    vector<vector<int>> lap;
    vector<int> path;
    vector<int> bestpath;
    int used = 0;
    int bestlen = INT_MAX/2;
    void preprocess(vector<string>& A) {
        for (int i=0; i<A.size(); ++i) {
            for (int j=0; j<A.size(); ++j) {
                lap[i][j] = getlen(A[i], A[j]);
            }
        }
    }
    
     int getlen(string cur, string A) {
        int len = min(cur.length(), A.length());
        while(len > 0) {
            if (cur.substr(cur.length()-len) != A.substr(0, len)) {
                len--;
            }else {
                break;
            }
        }
        return len;
    }
};

动态规划:

class Solution {
public:
    string shortestSuperstring(vector<string>& A) {
        n = A.size();
        dp = vector<vector<int> > ((1<<n), vector<int>(n, INT_MAX/2));
        cost = vector<vector<int> > (n, vector<int>(n));
        parent = vector<vector<int> > ((1<<n), vector<int>(n, -1));
        
        // init
        preprocess(A);
        for (int i=0; i<n; ++i) {
            dp[(1<<i)][i] = A[i].length();
        }
        
        // transaction
        for (int i=1; i<(1<<n); ++i) {
            int s = i;
            for (int j=0; j<n; ++j) {
                if (!(s & (1<<j))) continue;
                int ps = s & ~(1<<j);
                for (int k=0; k<n; ++k) {
                    if (dp[ps][k] + cost[k][j] < dp[s][j]) {
                        dp[s][j] = dp[ps][k] + cost[k][j];
                        parent[s][j] = k;
                    }
                }
            }
        }
        
        int s = (1<<n)-1;
        auto it = min_element(dp[s].begin(), dp[s].end());
        int cur = it - dp[s].begin();
        string ans = "";
       
        while(s > 0) {
            int parentId = parent[s][cur];
            ans = A[cur].substr((parentId == -1 ? 0 : A[cur].length()-cost[parentId][cur])) + ans;
            s -= (1<<cur);
            cur = parentId;
        }
        return ans;
    }
    
private:
    int n;
    vector<vector<int>> cost;
    vector<vector<int>> dp;
    vector<vector<int>> parent;
    
    void preprocess(vector<string>& A) {
        for (int i=0; i<n; ++i) {
            for (int j=0; j<n; ++j) {
                cost[i][j] = getcost(A[i], A[j]);
            }
        }
    }
    
    int getcost(string& A, string& B) {
        int len = min(A.length(), B.length());
        while(len > 0) {
            if (A.substr(A.length()-len) != B.substr(0, len))
                len--;
            else break;
        }
        return B.length()-len;
    }
};

用二进制位来表示状态,并且进行状态转移的思路。
dfs时,尽量优化。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!