Given an array A of strings, find any smallest string that contains each string in A as a substring.java
We may assume that no string in A is substring of another string in A.node
Example 1:算法
Input: ["alex","loves","leetcode"]app
Output: "alexlovesleetcode"ui
Explanation: All permutations of "alex","loves","leetcode" would also be accepted.code
2019.1.19算法群里的题目:这道题涉及到旅行商问题,解法是状态压缩dpleetcode
参考大神的解法,梳理了下解法;字符串
给一个字符串序列,找到包含序列中全部字符串的最小的字符串get
(1)先建图:预处理计算出Cost,也就是一个节点到另外一个节点的权重,这里的话,,g[i][j]
表示将A[j]加到A[i]的后面,路径的长度:string
(2) 题目转化为找一条访问过每一个节点一次的最短的路径
dp[s][i] :表示访问了节点集合s,且最后一个节点是i的最短的路径,
注意这里s是一个Binary String,表示哪些节点已经访问过了,为1的节点是已经访问过的;
Assume we want to travel nodes: {n1, n2, n3, n4} then
i = 2 ^ n1 +2 ^ n2 +2 ^ n3 +2 ^ n4;
path[s][i]:表示访问了节点集合s,而且最后一个节点是i,i前面的一个节点;
记录路径中,i的前面的一个点,以便于重建途径的时候的回溯
(4) dp更新策略:
dp[s][i] = min(dp[s - 2^i][j] + g[j][i]) #将j加到i的后面
/** 题意:给一个字符串序列,找到包含序列中全部字符串的最小的子串 分析: */ class Solution { public String shortestSuperstring(String[] A) { int n = A.length; int[][] graph = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { graph[i][j] = calCost(A, i, j); //把j加到i后面的路径长度,即图中i->j的边长 graph[j][i] = calCost(A, j, i); } } int[][] dp = new int[1 << n][n]; //dp[s][i] :表示访问了节点集合s,且最后一个节点是i的最短的路径 int[][] path = new int[1 << n][n]; int min = Integer.MAX_VALUE, last = -1; for (int state = 1; state < (1 << n); state++) { //枚举全部的节点集合组成状态 Arrays.fill(dp[state], Integer.MAX_VALUE); for (int node = 0; node < n; node++) { if ((state & (1 << node)) > 0) { //判断node在不在节点集合中 int leftState = state - (1 << node); //剩下的节点集合 if (leftState == 0) { //若是只剩一个节点了,则当前长度就是node的长度 dp[state][node] = A[node].length(); } else { for (int k = 0; k < n; k++) { //dp更新 if (dp[leftState][k] != Integer.MAX_VALUE && dp[leftState][k] + graph[k][node] < dp[state][node]) { //若是访问过了leftState且通过k点的路径更小,则更 dp[state][node] = dp[leftState][k] + graph[k][node]; path[state][node] = k; } } } } if (state == (1 << n) - 1 && dp[state][node] < min) { min = dp[state][node]; last = node; } //System.out.println(dp[state][node]); } } //创建路径 StringBuilder sb = new StringBuilder(); int cur = (1 << n) - 1; Stack<Integer> stack = new Stack<>(); while (cur > 0) { stack.push(last); int temp = cur; cur -= (1 << last); last = path[temp][last]; } int i = stack.pop(); sb.append(A[i]); while (!stack.isEmpty()) { int j = stack.pop(); sb.append(A[j].substring(A[j].length() - graph[i][j])); i = j; } return sb.toString(); } private int calCost(String[] A, int i, int j) { for (int pos = 1; pos < A[i].length(); pos++) { if (A[j].startsWith(A[i].substring(pos))) { return A[j].length() - A[i].length() + pos; } } return A[j].length(); } }