1. 程式人生 > >LeetCode周賽#111 Q4 Find the Shortest Superstring(狀壓DP)

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. 1 <= A.length <= 12
  2. 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);
	}
};