最短公共超序列

题目描述

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

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

示例:

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

提示:

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/shortest-common-supersequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

动态规划
定义dp[str1.length()+1][str2.length()+1]
状态:dp[i][j]表示以str1(i~末尾)和str2(j-末尾)作为子序列的最短字符串长度。
初始值:
dp数组从后往前构建
列边界值表示以str1子串和空字符串作为子序列的最短字符串长度,毫无疑问是str1子串的长度;
同理行边界值表示以空字符串和str2的子串作为子序列的最短字符串长度,毫无疑问是str2子串的长度。
状态转移方程:
如果str1.charAt(i)==str2.charAt(j)
dp[i][j]=dp[i+1][j+1]+1;
否则
dp[i][j]=Math.min(dp[i+1][j],dp[i][j+1])+1;
因为我们要求的是字符串
所以在构建dp的同时,可以把dp对应的string也定义出来。
定义strdp[m+1][n+1];

代码

class Solution {
   public String shortestCommonSupersequence(String str1, String str2) {
       int m = str1.length();
       int n = str2.length();
       int[][] dp = new int[m+1][n+1];
       String[][] strdp = new String[m+1][n+1];
       for(int i=0;i<m+1;i++){
           dp[i][n]=m-i;
           if(i!=m){
               strdp[i][n]=str1.substring(i);
           }else{
               strdp[i][n]="";
           }
       }
       for(int j=0;j<n+1;j++){
           dp[m][j]=n-j;
           if(j!=n){
               strdp[m][j]=str2.substring(j);
           }else{
               strdp[m][j]="";
           }
       }
       for(int i=m-1;i>=0;i--){
           for(int j=n-1;j>=0;j--){
               if(str1.charAt(i)==str2.charAt(j)){
                   dp[i][j]=dp[i+1][j+1]+1;
                   strdp[i][j]=str1.charAt(i)+strdp[i+1][j+1];
               }else{
                   //选右边
                   if(dp[i][j+1]<dp[i+1][j]){
                       dp[i][j]=dp[i][j+1]+1;
                       strdp[i][j]=String.valueOf(str2.charAt(j))+strdp[i][j+1];
                   }else{
                       //选下边
                       dp[i][j]=dp[i+1][j]+1;
                       strdp[i][j]=String.valueOf(str1.charAt(i))+strdp[i+1][j];
                   }
               }
           }
       }
       return strdp[0][0];
   }
}

发现超内存了
优化,不把每个strdp都求出来,只要求最终的即可,通过dp构建最终的字符串。

StringBuilder res = new StringBuilder();
        int t1 = 0, t2 = 0;
        while (t1 < m && t2 < n) {
            if (str1.charAt(t1) == str2.charAt(t2)) {
                res.append(str1.charAt(t1));
                ++t1;
                ++t2;
            } else if (dp[t1 + 1][t2] == dp[t1][t2] - 1) {
                res.append(str1.charAt(t1));
                ++t1;
            } else if (dp[t1][t2 + 1] == dp[t1][t2] - 1) {
                res.append(str2.charAt(t2));
                ++t2;
            }
        }
        if (t1 < m) {
            res.append(str1.substring(t1));
        } else if (t2 < n) {
            res.append(str2.substring(t2));
        }
        return res.toString();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值