1092. 最短公共超序列

题目链接:1092. 最短公共超序列

方法一:转换为lcs(最长公共子序列)问题进行求解

解题思路

先得到两个字符串\(lcs\),然后再计算\(ans\)

  1. \(lcs\)的获取:最长公共子序列 编辑距离【基础算法精讲 19】
    动态规划的\(dp\)数组在本题有两种实现方式
    (1)vector<vector<string>> dp(n + 1, vector<string>(m + 1));\(dp[i][j]\)表示\(str1[0, i]\)\(str2[0, j]\)的最长公共子序列,\(lcs = dp[n][m]\)
    (2)vector<vector<int>> dp(n + 1, vector<int>(m + 1));\(dp[i][j]\)表示\(str1[0, i]\)\(str2[0, j]\)的最长公共子序列的长度,然后再回溯\(dp\)数组的值得到\(lcs\)
// 方式一
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] + str1[i - 1];
        } else {
            if (dp[i][j - 1].length() > dp[i - 1][j].length()) {
                dp[i][j] += dp[i][j - 1];
            } else {
                dp[i][j] += dp[i - 1][j];
            }
        }
    }
}
string lcs = dp[n][m];

// 方式二
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 lcs;
int i = n, j = m;
while (dp[i][j]) { 
    if (dp[i - 1][j] == dp[i][j]) {
        i -- ;
    } else if (dp[i][j - 1] == dp[i][j]) {
        j -- ;
    } else {
        lcs = str1[i - 1] + lcs;
        i -- ;
        j -- ;
    }
}
  1. \(ans\)计算
    本题要求\(ans\)包含子序列\(str1\)\(str2\),且要求\(ans\)最短,那么\(ans\)只需要包含两个字符串的\(lcs\)以及其各自不在\(lcs\)中的字符即可,并且保证各自在\(ans\)中两个字符串的字符相对位置保持不变
string ans;
        int idx = lcs.length(), k = 0;
        i = 0, j = 0;
        while (k < idx) {
            while (str1[i] != lcs[k]) { // 将str1中lcs[k]之前的字符加入ans
                ans += str1[i];
                i ++ ;
            }
            while (str2[j] != lcs[k]) { // 将str2中lcs[k]之前的字符加入ans
                ans += str2[j];
                j ++ ;
            }
            ans += lcs[k]; // 等两个字符串中在lcs[k]之前的字符加入后,再将lcs[k]加入ans中,然后重复上述操作
            k ++ ;
            i ++ ;
            j ++ ;
        }
        while (i < n) ans += str1[i ++ ];
        while (j < m) ans += str2[j ++ ];

代码

class Solution {
public:
    string shortestCommonSupersequence(string str1, string str2) {
        // 计算str1和str2的lcs的dp数组
        int n = str1.length(), m = str2.length();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        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]);
                }
            }
        }
        // 根据dp数组得出lcs
        string lcs;
        int i = n, j = m;
        while (dp[i][j]) { 
            if (dp[i - 1][j] == dp[i][j]) {
                i -- ;
            } else if (dp[i][j - 1] == dp[i][j]) {
                j -- ;
            } else {
                lcs = str1[i - 1] + lcs;
                i -- ;
                j -- ;
            }
        }
        // 根据lcs得出答案
        string ans;
        int idx = lcs.length(), k = 0;
        i = 0, j = 0;
        while (k < idx) {
            while (str1[i] != lcs[k]) {
                ans += str1[i];
                i ++ ;
            }
            while (str2[j] != lcs[k]) {
                ans += str2[j];
                j ++ ;
            }
            ans += lcs[k];
            k ++ ;
            i ++ ;
            j ++ ;
        }
        while (i < n) ans += str1[i ++ ];
        while (j < m) ans += str2[j ++ ];
        return ans;
    }
};

复杂度分析

时间复杂度:\(O(nm)\)
空间复杂度:\(O(nm)\)

方法二:动态规划

解题思路

\(dp[n + 1][m + 1]\)\(dp[i][j]\)表示\(str1[0, i)\)\(str2[0, j)\)的最短公共超序列的长度。
对于\(dp[i][j]\)

  1. \(str1[i - 1] == str2[j - 1]\),则\(dp[i][j] = dp[i - 1][j - 1] + 1\)
  2. 否则,\(dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1\)

然后再回溯求\(str1[0, n + 1) 和 str2[0, m + 1)\)的最短公共超序列。

代码

class Solution {
public:
    string shortestCommonSupersequence(string str1, string str2) {
        int n = str1.length(), m = str2.length();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i ++ ) dp[i][0] = i;
        for (int j = 1; j <= m; j ++ ) dp[0][j] = j;
        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] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
                }
            }
        }
        string ans;
        while (dp[n][m]) {
            if (n - 1 >= 0 && m - 1 >= 0 && str1[n - 1] == str2[m - 1] && dp[n][m] == dp[n - 1][m - 1] + 1) {
                ans = str1[n - 1] + ans;
                n -- ;
                m -- ;
            } else if (n - 1 >= 0 && dp[n][m] == dp[n - 1][m] + 1) {
                ans = str1[n - 1] + ans;
                n -- ;
            } else if (m - 1 >= 0 && dp[n][m] == dp[n][m - 1] + 1){
                ans = str2[m - 1] + ans;
                m -- ;
            }
        }
        return ans;
    }
};

复杂度分析

时间复杂度:\(O(nm)\)
空间复杂度:\(O(nm)\)

posted @ 2023-04-09 01:02  lixycc  阅读(118)  评论(0)    收藏  举报