题目描述
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时,尽量优化。
来源:CSDN
作者:涛涛酱
链接:https://blog.csdn.net/iCode_girl/article/details/104652939