题目来源: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);
}
};