AlgoMaster Logo

Shortest Common Supersequence

hardFrequencyUpdated September 21, 2026

Understanding the Problem

We need to build the shortest possible string that contains both str1 and str2 as subsequences. The task is to merge the two strings while preserving the relative order of characters from each, using as few total characters as possible.

Concatenating the two strings gives a valid supersequence of length m + n, but we can do better. Characters that appear in both strings, in a consistent order, only need to appear once in the result. So the problem reduces to finding the longest common subsequence (LCS) of the two strings, then arranging the remaining characters around it.

If the LCS has length L, the shortest common supersequence has length m + n - L. Each of the L shared characters is written once instead of twice, and every non-shared character is written exactly once.

Key Constraints:

  • 1 <= str1.length, str2.length <= 1000 → An O(m * n) DP solution fills at most 1,000,000 cells, well within limits. An exponential brute force is ruled out beyond very short inputs.
  • Lowercase English letters only → no special handling for character sets, and the result fits comfortably in standard string types.

Approach 1: Brute Force (Generate All Supersequences)

Intuition

Build the supersequence by merging the two strings character by character and explore every way to do it. With two pointers i and j tracking positions in str1 and str2, each step takes the next character from str1, the next character from str2, or, when those two characters match, takes one copy that advances both pointers.

When the current characters match, taking one shared copy is always at least as good as taking either separately, so there is no need to branch in that case. When they differ, we try both options and keep whichever recursion returns the shorter string. Exploring every branch this way guarantees we find a shortest supersequence, but the work grows exponentially.

Algorithm

  1. Use two pointers i and j, starting at 0, to track positions in str1 and str2.
  2. If i has reached the end of str1, append the rest of str2 and return.
  3. If j has reached the end of str2, append the rest of str1 and return.
  4. If str1[i] == str2[j], take that character once and recurse with i+1, j+1.
  5. Otherwise, try two branches: take str1[i] and recurse with i+1, j, or take str2[j] and recurse with i, j+1. Return whichever gives the shorter result.
  6. The answer is the result of calling this from i=0, j=0.

Visualization and Code

Loading animation...

This becomes too slow once the strings exceed about 15 characters, because the same subproblem (i, j) is recomputed many times across different branches. The next approach removes that redundancy with dynamic programming, building the answer from a table instead of re-deriving it.

Approach 2: LCS-Based DP with Traceback

Intuition

The shortest common supersequence is built directly from the Longest Common Subsequence (LCS). A character that appears in the LCS only needs to appear once in the supersequence, because that single copy satisfies both str1 and str2 at the same time. Every other character appears once on its own. That makes the length m + n - LCS_length.

We need the actual string, not just its length. Build the standard LCS DP table, then trace back through it to construct the supersequence one character at a time. When the two current characters match (a diagonal move in the table), include that character once. When they differ, follow the direction that the DP table grew from, up or left, and include the corresponding character.

Algorithm

  1. Build a 2D DP table where dp[i][j] = length of LCS of str1[0..i-1] and str2[0..j-1].
  2. Fill the table: if str1[i-1] == str2[j-1], then dp[i][j] = dp[i-1][j-1] + 1. Otherwise, dp[i][j] = max(dp[i-1][j], dp[i][j-1]).
  3. Trace back from dp[m][n] to build the supersequence:
    • If str1[i-1] == str2[j-1], add this character and move diagonally (i-1, j-1).
    • If dp[i-1][j] >= dp[i][j-1], add str1[i-1] and move up (i-1, j).
    • Otherwise, add str2[j-1] and move left (i, j-1).
  4. Once one pointer reaches 0, append the remaining characters from the other string.
  5. Reverse the built string.

Visualization and Code

Loading animation...

The next approach reaches the same answer in two separate phases: first recover the LCS string, then merge both inputs around it. The complexity is identical, but the two steps can be reasoned about independently.

Approach 3: LCS + Merge (Two-Phase Construction)

Intuition

This approach separates the two concerns. First find the actual LCS string, not just its length. Then use the LCS as a skeleton and merge both input strings around it. For each LCS character, include all characters from str1 and str2 that come before it (the non-shared characters), then include the LCS character once.

The result matches Approach 2. The advantage is that the LCS recovery and the merge are independent steps, each short enough to verify on its own.

Algorithm

  1. Build the LCS DP table and trace back to get the actual LCS string.
  2. Initialize three pointers: i for str1, j for str2, and iterate through each character c of the LCS.
  3. For each LCS character c:
    • Append all characters from str1 (advancing i) until str1[i] == c.
    • Append all characters from str2 (advancing j) until str2[j] == c.
    • Append c once and advance i, j past it.
  4. After the LCS is exhausted, append any remaining characters from str1 and str2.

Visualization and Code

Loading animation...