LeetCode周賽#111 Q4 Find the Shortest Superstring(狀壓DP)
題目來源:https://leetcode.com/contest/weekly-contest-111/problems/find-the-shortest-superstring/
問題描述
943. Find the Shortest Superstring
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
------------------------------------------------------------
題意
定義一個字串集合S的母串sp為一個使得∀s∈S, s==substr(sp) . 給定一組字串,已知沒有任何一個字串是另一個的子串,求這組字串的最短母串(如有多個,給出任意一個即可)。
------------------------------------------------------------
思路
以字串為節點,字串之間的共同字首字尾長度為邊,建立有向圖。問題轉化為求有向圖的最長哈密頓道路
建圖過程:對於字串集合中的字串a與字串b, 如果a[-i:] == b[0:i],則從a向b連一條長度為i的有向邊,表示a的字尾與b的字首共用,b在a後;同理,如果a[0:j] == b[-j:],則從b向a連一條長度為j的有向邊,表示b的字尾與a的字首共用,a在b後。
最長哈密頓道路:回憶最短哈密頓道路的題目POJ 3311 Hie with the Pie(狀壓DP),我們用狀態壓縮動態規劃求解。同樣,最長哈密頓道路也可以用狀壓DP求解。用dp[s][i]表示經過的節點組成的狀態為s的時候,到達節點i所經過的長度,其中(s & (1<<j))表示狀態s是否經過節點j. 從1到(1<<n)-1遍歷狀態s,最終取dp[(1<<n)-1][i](i=0,1,…,n-1)中的最大者,就是最長哈密頓道路。
回溯:由於題目要求輸出路徑,所以求出最長哈密頓道路的長度之後,還要回溯找到最長哈密頓道路的各個節點的順序,按順序輸出。採用的方法是令dp陣列的元組為一個類,類屬性包括道路長度和前驅節點,並置起始節點的前驅為-1,反向回溯直至遇到-1節點,可以得到逆序的路徑,再逆序輸出即可。
------------------------------------------------------------
程式碼
class Solution {
public:
const static int NMAX = 12;
class node {
public:
int len, pre; // len: length of Hamilton path; pre: pre string id
node(void) : len(0), pre(-1) {}
node(int len, int pre) : len(len), pre(pre) {}
};
int n;
int mat[NMAX][NMAX] = {};
node dp[1 << NMAX][NMAX];
void build_graph(vector<string>& A)
{
n = A.size();
int i, j, k, si, sj;
for (i = 0; i < n-1; i++)
{
for (j = i + 1; j < n; j++)
{
si = A[i].size();
sj = A[j].size();
for (k = 0; k < si; k++)
{
string s = A[i].substr(k);
if (s == A[j].substr(0, si - k))
{
mat[i][j] = si - k;
break;
}
}
for (k = 0; k < sj; k++)
{
string s = A[j].substr(k);
if (s == A[i].substr(0, sj - k))
{
mat[j][i] = sj - k;
break;
}
}
}
}
}
void hamilton()
{
int s, i, j, lmax, imax;
for (s = 1; s < (1 << n); s++)
{
for (i = 0; i < n; i++)
{
lmax = 0;
imax = -1;
if (s & (1 << i))
{
for (j = 0; j < n; j++)
{
if (i != j && (s & (1<<j))
&& dp[s - (1 << i)][j].len + mat[j][i] >= lmax)
{
lmax = dp[s - (1 << i)][j].len + mat[j][i];
imax = j;
}
}
}
dp[s][i].len = lmax;
dp[s][i].pre = imax;
}
}
}
string recall(vector<string>& A)
{
string ret;
vector<int> seq;
int i, lmax = 0, s = (1<<n) - 1, j;
for (i = 0; i < n; i++)
{
if (dp[s][i].len >= lmax)
{
lmax = dp[s][i].len;
j = i;
}
}
while (j != -1)
{
seq.push_back(j);
i = j;
j = dp[s][j].pre;
s -= (1 << i);
}
ret.append(A[seq[n - 1]]);
for (i = n - 2; i >= 0; i--)
{
ret.append(A[seq[i]].substr(mat[seq[i + 1]][seq[i]]));
}
return ret;
}
string shortestSuperstring(vector<string>& A) {
if (A.size() == 1)
{
return A[0];
}
build_graph(A);
hamilton();
return recall(A);
}
};