Skip to content

943. Find the Shortest Superstring

Description

Given an array of strings words, return the smallest string that contains each string in words as a substring. If there are multiple valid strings of the smallest length, return any of them.

You may assume that no string in words is a substring of another string in words.

 

Example 1:

Input: words = ["alex","loves","leetcode"]
Output: "alexlovesleetcode"
Explanation: All permutations of "alex","loves","leetcode" would also be accepted.

Example 2:

Input: words = ["catg","ctaagt","gcta","ttca","atgcatc"]
Output: "gctaagttcatgcatc"

 

Constraints:

  • 1 <= words.length <= 12
  • 1 <= words[i].length <= 20
  • words[i] consists of lowercase English letters.
  • All the strings of words are unique.

 

Solutions

Solution: Dynamic Programming

  • Time complexity: O(n * 2n)
  • Space complexity: O(n * 2n)

 

JavaScript

js
/**
 * @param {string[]} words
 * @return {string}
 */
const shortestSuperstring = function (words) {
  const n = words.length;
  const costs = setupCosts(words, n);
  const dp = initializeDP(words, n);
  const parent = Array.from({length: 1 << n})
    .fill('')
    .map(_ => new Array(n).fill(-1));

  setupCosts(words, n);
  fillDPandParent(dp, parent, costs, n);

  let result = '';
  const dpBack = dp[(1 << n) - 1];
  let a = dpBack.indexOf(Math.min(...dpBack));
  let mask = (1 << n) - 1;

  while (mask > 0) {
    const index = parent[mask][a];

    if (index === -1) {
      result = words[a] + result;
    } else {
      result = words[a].slice(words[a].length - costs[index][a]) + result;
    }
    mask -= 1 << a;
    a = index;
  }
  return result;
};

function setupCosts(words, n) {
  const costs = new Array(n)
    .fill('')
    .map(_ => new Array(n).fill(0));

  const getCost = (a, b) => {
    const minLength = Math.min(a.length, b.length);
    let cost = b.length;

    for (let index = 1; index <= minLength; index++) {
      if (a.slice(-index) !== b.slice(0, index)) continue;
      cost = b.length - index;
    }
    return cost;
  };

  for (let a = 0; a < n; a++) {
    for (let b = 0; b < n; b++) {
      costs[a][b] = getCost(words[a], words[b]);
      costs[b][a] = getCost(words[b], words[a]);
    }
  }
  return costs;
}

function initializeDP(words, n) {
  const dp = Array.from({length: 1 << n})
    .fill('')
    .map(() => new Array(n).fill(Number.MAX_SAFE_INTEGER));

  for (let i = 0; i < n; i++) {
    dp[1 << i][i] = words[i].length;
  }
  return dp;
}

function fillDPandParent(dp, parent, costs, n) {
  for (let mask = 1; mask < 1 << n; mask++) {
    for (let a = 0; a < n; a++) {
      if ((mask & (1 << a)) === 0) continue;

      for (let b = 0; b < n; b++) {
        const cost = dp[mask - (1 << a)][b] + costs[b][a];

        if (cost >= dp[mask][a]) continue;
        dp[mask][a] = cost;
        parent[mask][a] = b;
      }
    }
  }
}

Released under the MIT license