算法小练习之求最短公共超序列

110 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

1、前言

每天一个算法小练习,本篇使用Java实现。

2、题目描述

  给出两个字符串 str1 和 str2,返回同时以 str1 和 str2 作为子序列的最短字符串。如果答案不止一个,则可以返回满足条件的任意一个答案。

  如果从字符串 T 中删除一些字符(也可能不删除,并且选出的这些字符可以位于 T 中的 任意位置),可以得到字符串 S,那么 S 就是 T 的子序列。

2.1、示例1

输入:str1 = "abac", str2 = "cab"
输出:"cabac"
解释:
str1 = "abac" 是 "cabac" 的一个子串,因为我们可以删去 "cabac" 的第一个 "c"得到 "abac"。 
str2 = "cab" 是 "cabac" 的一个子串,因为我们可以删去 "cabac" 末尾的 "ac" 得到 "cab"。
最终我们给出的答案是满足上述属性的最短字符串。

2.2、提示

  • 1 <= str1.length, str2.length <= 1000
  • 2 str1 和 str2 都由小写英文字母组成。

3、解题思路

动态规划:先求出最长公共子序列长度,然后进行归并组成超序列。

3.1、实现代码

public String shortestCommonSupersequence(String str1, String str2) {
    if (str1 == null || str2 == null) {
        return null;
    }
    int n = str1.length();
    int m = str2.length();
    /**
    * dp[i][j]表示字符串1的前i位进行扩充直到包含字符串2的前j位的最小方案数
    * 集合划分依据就是当前最后一个字符是否相等
    */
    String[][] dp = new String[n + 1][m + 1];
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            dp[i][j] = "";
        }
    }
    // dp 找最长公共子序列,dp 的核心思想就是前面的都已经匹配好了,只需要考虑当前的
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            if (str1.charAt(i - 1) == str2.charAt(j - 1)){
                // 如果当前字符相等那么说明不需要扩充,结果仍然是去掉当前这位的结果
                dp[i][j] = dp[i - 1][j - 1] + str1.charAt(i - 1);
            } else {
                //考虑既要包含a又要包含b的情况。可以保证a已经包含b是f[i-1][j],可以保证a还差一个字符才包含b的情况f[i][j-1]+1
                dp[i][j] = (dp[i - 1][j].length() > dp[i][j - 1].length() ? dp[i - 1][j] : dp[i][j - 1]);
            }
                
        }

    String lcs = dp[n][m];
    StringBuilder ret = new StringBuilder();
    int i = 0, j = 0;
    // 开始进行归并
    for (int k = 0; k < lcs.length(); k++) {
        // 先将 str1 和 str2 都归并到第k个公共字符
        char ch = lcs.charAt(k);
        while (i < n && str1.charAt(i) != ch) {
            ret.append(str1.charAt(i++));
        }
        while (j < m && str2.charAt(j) != ch) {
            ret.append(str2.charAt(j++));
        }
        // 加公共字符
        ret.append(ch);
        ++i;
        ++j;
    }

    //加上每个字符串在LCS之后的字符
    return ret.append(str1.substring(i) + str2.substring(j)).toString();
}

3.2、执行结果

image.png

好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊