Shortest Common Supersequence
Algorithm · Stretch
Step through it: Dynamic Programming pattern
Step 1 of 6
Goal: how many ways can you climb 5 stairs taking 1 or 2 steps at a time? You reach stair 5 from stair 4 or stair 3, so ways(5) = ways(4) + ways(3). Working that out naively repeats the same sub-questions many times. Instead write each answer down once.
Step 2 of 6
Base cases: there is 1 way to be on stair 0 (do nothing) and 1 way to reach stair 1.
Step 3 of 6
Stair 2 is reached from stair 1 or stair 0.
Step 4 of 6
Stair 3 comes from stairs 2 and 1.
Step 5 of 6
Stair 4: 3 + 2 = 5. Stair 5: 5 + 3 = 8. Each answer reuses two stored answers, so n stairs take n steps instead of exponential time. Storing sub-answers like this is dynamic programming.
Step 6 of 6
Your turn.
The shortest common supersequence (SCS) of two sequences X and Y
is the shortest sequence which has X and Y as subsequences.
In other words assume we're given two strings str1 and str2, find the shortest string that has both str1 and str2 as subsequences.
This is a problem closely related to the longest common subsequence problem.
Example
Input: str1 = "geek", str2 = "eke"
Output: "geeke"
Input: str1 = "AGGTAB", str2 = "GXTXAYB"
Output: "AGXGTXAYB"Code
import longestCommonSubsequence from '../longest-common-subsequence/longestCommonSubsequence';
/**
* @param {string[]} set1
* @param {string[]} set2
* @return {string[]}
*/
export default function shortestCommonSupersequence(set1, set2) {
// Let's first find the longest common subsequence of two sets.
const lcs = longestCommonSubsequence(set1, set2);
// If LCS is empty then the shortest common supersequence would be just
// concatenation of two sequences.
if (lcs.length === 1 && lcs[0] === '') {
return set1.concat(set2);
}
// Now let's add elements of set1 and set2 in order before/inside/after the LCS.
let supersequence = [];
let setIndex1 = 0;
let setIndex2 = 0;
let lcsIndex = 0;
let setOnHold1 = false;
let setOnHold2 = false;
while (lcsIndex < lcs.length) {
// Add elements of the first set to supersequence in correct order.
if (setIndex1 < set1.length) {
if (!setOnHold1 && set1[setIndex1] !== lcs[lcsIndex]) {
supersequence.push(set1[setIndex1]);
setIndex1 += 1;
} else {
setOnHold1 = true;
}
}
// Add elements of the second set to supersequence in correct order.
if (setIndex2 < set2.length) {
if (!setOnHold2 && set2[setIndex2] !== lcs[lcsIndex]) {
supersequence.push(set2[setIndex2]);
setIndex2 += 1;
} else {
setOnHold2 = true;
}
}
// Add LCS element to the supersequence in correct order.
if (setOnHold1 && setOnHold2) {
supersequence.push(lcs[lcsIndex]);
lcsIndex += 1;
setIndex1 += 1;
setIndex2 += 1;
setOnHold1 = false;
setOnHold2 = false;
}
}
// Attach set1 leftovers.
if (setIndex1 < set1.length) {
supersequence = supersequence.concat(set1.slice(setIndex1));
}
// Attach set2 leftovers.
if (setIndex2 < set2.length) {
supersequence = supersequence.concat(set2.slice(setIndex2));
}
return supersequence;
}References
Practice
Dynamic Programming
- Climbing StairsEasy
- Fibonacci NumberEasy
- Min Cost Climbing StairsEasy
- Pascal's TriangleEasy
- House RobberMedium
- House Robber IIMedium
- Coin ChangeMedium
- Coin Change IIMedium
- Longest Increasing SubsequenceMedium
- Longest Common SubsequenceMedium
- Word BreakMedium
- Unique PathsMedium
- Unique Paths IIMedium
- Minimum Path SumMedium
- Jump GameMedium
- Partition Equal Subset SumMedium
- Target SumMedium
- Decode WaysMedium
- Maximum Product SubarrayMedium
- Longest Palindromic SubstringMedium
- Palindromic SubstringsMedium
- Best Time to Buy and Sell Stock with CooldownMedium
- TriangleMedium
- Maximal SquareMedium
- Edit DistanceHard
- Regular Expression MatchingHard
- Burst BalloonsHard
- Distinct SubsequencesHard
- Longest Valid ParenthesesHard
- Best Time to Buy and Sell Stock IVHard
Spotted a mistake or something unclear? Report it on GitHub.