[LeetCode] 1092. Shortest Common Supersequence
Problem
https://leetcode.com/problems/shortest-common-supersequence/description/
Leetcode - Shortest Common Supersequence
Type - Dynamic programming, String, LCS(Longest Common Subsequence)
Difficulty - Hard
Approach & Solution
The worst case is string A+B or B+A(concatenation)
How can we shorten the length of the answer?
If we use common characters in str1 and str2 at once, no redundant characters are needed.
So, Longest Common Subsequence is needed.
data structures & variables:
dp[i][j]
: Length of LCS from between string(str1[0:i-1], str2[0:j-1]
)common
: common LCS.
Let dp[i][j]
is length of LCS from two substrings(str1[0:i-1], str2[0:j-1]
).
Iterate 2D loops by doing:
If
str1[i-1] == str2[j-1], dp[i][j] = dp[i-1][j-1] + 1.
Else,
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
.
Then, backtrack to recover LCS.
Initialize i = n, j = m
, backtrack while i
and j
are positive.
If
str1[i-1] == str2[j-1]
, appendstr1[i-1]
.Else if
dp[i-1][j] >= dp[i][j-1]
, reducei
by 1,Else, reduce
j
by 1. It means reduce boundary from greater one.
Since backtracking, LCS is given reversed.
So reverse the LCS.
Now, LCS is given, the last thing to do is to add exclusive characters in both string.
With two-pointer approach, set i = 0 and j = 0
, i
for str1
, j
for str2
.
For each character ch
in LCS:
add
str1[i]
to answer and increment one whilestr1[i] ≠ ch
. (add non-matching characters inadd
str2[j]
to answer and increment one whilestr2[j] ≠ ch
.now,
str1[i] and str2[j]
isch
. so, add ch and increment one for each i, j.
Finally, add suffixes: remaing str1[i:], str2[j:].
Complexity
Time Complexity: O(n*m) - Computing LSC with Bottom-Up DP.
- Plus, recovering LCS and constructing answer is O(n+m).
Space Complexity: O(n*m) - DP Matrix.
Code (C++ | Go)
class Solution {
public:
string shortestCommonSupersequence(string str1, string str2) {
int n = str1.size(), m = str2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (str1[i - 1] == str2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
string common = "";
int i = n, j = m;
while (i > 0 && j > 0) {
if (str1[i - 1] == str2[j - 1]) {
common.push_back(str1[i - 1]);
i--;
j--;
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
i--;
} else {
j--;
}
}
reverse(common.begin(), common.end());
string ans = "";
i = 0; j = 0;
for (char c : common) {
while (i < n && str1[i] != c) {
ans.push_back(str1[i]);
i++;
}
while (j < m && str2[j] != c) {
ans.push_back(str2[j]);
j++;
}
ans.push_back(c);
i++;
j++;
}
return ans + str1.substr(i) + str2.substr(j);
}
};
func reverse(s string) string {
n := len(s)
b := []byte(s)
for i := 0; i < n/2; i++ {
b[i], b[n-i-1] = b[n-i-1], b[i]
}
return string(b)
}
func shortestCommonSupersequence(str1 string, str2 string) string {
n := len(str1)
m := len(str2)
dp := make([][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, m+1)
}
for i := 1; i <= n; i++ {
for j := 1; j <= m; j++ {
if str1[i-1] == str2[j-1] {
dp[i][j] = dp[i-1][j-1] + 1
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
}
}
}
i := n
j := m
var common strings.Builder
for i > 0 && j > 0 {
if str1[i-1] == str2[j-1] {
common.WriteByte(str1[i-1])
i--
j--
} else if dp[i-1][j] >= dp[i][j-1] {
i--
} else {
j--
}
}
commonStr := common.String()
reversed := reverse(commonStr)
i = 0
j = 0
ans := ""
for _, c := range reversed {
for i < n && str1[i] != byte(c) {
ans += string(str1[i])
i++
}
for j < m && str2[j] != byte(c) {
ans += string(str2[j])
j++
}
ans += string(c)
i++
j++
}
for i < n {
ans += string(str1[i])
i++
}
for j < m {
ans += string(str2[j])
j++
}
return ans
}