Copy class Solution {
public String shortestSuperstring(String[] A) {
int V = A.length;
// 算每个string a和其他string b相连之后增加的 length
int[][] dist = new int[V][V];
for (int i = 0; i < V; ++i) {
for (int j = 0; j < V; ++j) {
String a = A[i], b = A[j]; // 拼ab,算长度
int commonLen = Math.min(a.length(), b.length());
while (commonLen > 0 &&
!a.substring(a.length() - commonLen).equals(b.substring(0, commonLen))) {
commonLen--;
}
dist[i][j] = b.length() - commonLen;
// System.out.println(a + " " + b + " " + dist[i][j]);
}
}
// dp[i][j] 表示以 binary mask i 表示的前缀节点组合并且以 A[j] 结尾的路径,
// 其最小长度为 dp[i][j][0]
// dp[i][j][1] 存放的是parent的index,因为我们最终要找到一条最短路径
int[][][] dp = new int[1<<V][V][2];
// initialization
for (int i = 0; i < dp.length; ++i) {
for (int j = 0; j < V; ++j) {
dp[i][j][0] = Integer.MAX_VALUE / 2;
dp[i][j][1] = -1;
}
}
for (int i = 0; i < V; ++i) {
dp[1<<i][i][0] = A[i].length();
}
// 每种前缀节点组合
for (int s = 0; s < (1<<V); ++s) {
// 考虑每个node作为当前结尾
for (int i = 0; i < V; ++i) {
if (((1 << i) & s) == 0) continue; // 该组合s中没有node i
int prev = s & (~(1 << i)); // 之前的组合,不包括i
// 考虑每个node为之前的结尾
for (int j = 0; j < V; ++j) {
if (i == j || ((1 << j) & prev) == 0) continue;
if (dp[prev][j][0] + dist[j][i] < dp[s][i][0]) {
dp[s][i][0] = dp[prev][j][0] + dist[j][i];
dp[s][i][1] = j;
}
// System.out.println(i + " " + A[i] + " " + A[j] + " " + dp[s][i][0] + " " + dist[j][i]);
}
}
}
// 在dp[(1<<V)-1](即所有node都被使用的组合)中找出最小的长度,并利用dp[i][j][1]得到path
int minLen = Integer.MAX_VALUE / 2, minEndNode = -1, s = (1 << V) - 1;
for (int i = 0; i < V; ++i) {
if (dp[s][i][0] < minLen) {
minLen = dp[s][i][0];
minEndNode = i;
}
}
String res = "";
int curr = minEndNode;
// System.out.println(A[minEndNode] + " " + minLen);
while (s > 0) {
int prev = dp[s][curr][1];
if (prev < 0) {
res = A[curr] + res;
return res;
} else {
int len = dist[prev][curr];
res = A[curr].substring(A[curr].length() - len) + res;
}
s &= ~(1 << curr);
curr = prev;
}
return res;
}
}