LeetCode Solutions
943. Find the Shortest Superstring
Time: $O(n!)$ Space:
class Solution {
public:
string shortestSuperstring(vector<string>& A) {
const int n = A.size();
// cost[i][j] := cost to append A[j] after A[i]
vector<vector<int>> cost(n, vector<int>(n));
// GetCost(a, b) := cost to append b after a
auto getCost = [](const string& a, const string& b) {
int cost = b.length();
const int minLength = min(a.length(), b.length());
for (int k = 1; k <= minLength; ++k)
if (a.substr(a.length() - k) == b.substr(0, k))
cost = b.length() - k;
return cost;
};
// Pre-calculate cost array to save time
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
cost[i][j] = getCost(A[i], A[j]);
cost[j][i] = getCost(A[j], A[i]);
}
vector<int> bestPath;
int minLength = n * 20; // Given by problem
dfs(A, cost, {}, bestPath, 0, 0, 0, minLength);
string ans = A[bestPath[0]];
for (int k = 1; k < n; ++k) {
const int i = bestPath[k - 1];
const int j = bestPath[k];
ans += A[j].substr(A[j].length() - cost[i][j]);
}
return ans;
}
private:
// Used: i-th bit means A[i] is used or not
void dfs(const vector<string>& A, const vector<vector<int>>& cost,
vector<int>&& path, vector<int>& bestPath, int used, int depth,
int currLength, int& minLength) {
if (currLength >= minLength)
return;
if (depth == A.size()) {
minLength = currLength;
bestPath = path;
return;
}
for (int i = 0; i < A.size(); ++i) {
if (1 << i & used)
continue;
path.push_back(i);
const int newLength =
depth == 0 ? A[i].length() : currLength + cost[path[depth - 1]][i];
dfs(A, cost, move(path), bestPath, used | 1 << i, depth + 1, newLength,
minLength);
path.pop_back();
}
}
};
class Solution {
public String shortestSuperstring(String[] A) {
final int n = A.length;
// cost[i][j] := cost to append A[j] after A[i]
int[][] cost = new int[n][n];
// Pre-calculate cost array to save time
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
cost[i][j] = getCost(A[i], A[j]);
cost[j][i] = getCost(A[j], A[i]);
}
List<Integer> path = new ArrayList<>();
List<Integer> bestPath = new ArrayList<>();
minLength = n * 20; // Given by problem
dfs(A, cost, path, bestPath, 0, 0, 0);
StringBuilder sb = new StringBuilder(A[bestPath.get(0)]);
for (int k = 1; k < n; ++k) {
final int i = bestPath.get(k - 1);
final int j = bestPath.get(k);
sb.append(A[j].substring(A[j].length() - cost[i][j]));
}
return sb.toString();
}
private int minLength;
// GetCost(a, b) := cost to append b after a
private int getCost(final String a, final String b) {
int cost = b.length();
final int minLength = Math.min(a.length(), b.length());
for (int k = 1; k <= minLength; ++k)
if (a.substring(a.length() - k).equals(b.substring(0, k)))
cost = b.length() - k;
return cost;
}
// Used: i-th bit means A[i] is used or not
private void dfs(String[] A, int[][] cost, List<Integer> path, List<Integer> bestPath, int used,
int depth, int currLength) {
if (currLength >= minLength)
return;
if (depth == A.length) {
minLength = currLength;
bestPath.clear();
for (final int node : path) {
bestPath.add(node);
}
return;
}
for (int i = 0; i < A.length; ++i) {
if ((1 << i & used) > 0)
continue;
path.add(i);
final int newLength = depth == 0 ? A[i].length() : currLength + cost[path.get(depth - 1)][i];
dfs(A, cost, path, bestPath, used | 1 << i, depth + 1, newLength);
path.remove(path.size() - 1);
}
}
}