From b5f5d13215de1e9c5476747b3186632f121dedef Mon Sep 17 00:00:00 2001 From: AC_Oier Date: Thu, 15 Sep 2022 14:24:26 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat:=20add=201092?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\345\217\214\346\214\207\351\222\210.md" | 1 + "Index/\345\272\217\345\210\227 DP.md" | 1 + "Index/\346\236\204\351\200\240.md" | 1 + ...10\345\233\260\351\232\276\357\274\211.md" | 172 ++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 "LeetCode/1091-1100/1092. \346\234\200\347\237\255\345\205\254\345\205\261\350\266\205\345\272\217\345\210\227\357\274\210\345\233\260\351\232\276\357\274\211.md" diff --git "a/Index/\345\217\214\346\214\207\351\222\210.md" "b/Index/\345\217\214\346\214\207\351\222\210.md" index 1ae497e8..2aea4f1f 100644 --- "a/Index/\345\217\214\346\214\207\351\222\210.md" +++ "b/Index/\345\217\214\346\214\207\351\222\210.md" @@ -47,6 +47,7 @@ | [1021. 删除最外层的括号](https://leetcode.cn/problems/remove-outermost-parentheses/) | [LeetCode 题解链接](https://leetcode.cn/problems/remove-outermost-parentheses/solution/by-ac_oier-jmxi/) | 简单 | 🤩🤩🤩🤩 | | [1052. 爱生气的书店老板](https://leetcode-cn.com/problems/grumpy-bookstore-owner/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/grumpy-bookstore-owner/solution/hua-dong-chuang-kou-luo-ti-by-ac_oier-nunu/) | 中等 | 🤩🤩🤩 | | [1089. 复写零](https://leetcode.cn/problems/duplicate-zeros/) | [LeetCode 题解链接](https://leetcode.cn/problems/duplicate-zeros/solution/by-ac_oier-zivq/) | 简单 | 🤩🤩🤩🤩 | +| [1092. 最短公共超序列](https://leetcode.cn/problems/shortest-common-supersequence/) | [LeetCode 题解链接](https://leetcode.cn/problems/shortest-common-supersequence/solution/by-ac_oier-s346/) | 困难 | 🤩🤩🤩 | | [1221. 分割平衡字符串](https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-wumnk/) | 简单 | 🤩🤩🤩🤩 | | [1332. 删除回文子序列](https://leetcode-cn.com/problems/remove-palindromic-subsequences/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/remove-palindromic-subsequences/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-0zwn/) | 简单 | 🤩🤩🤩🤩 | | [1417. 重新格式化字符串](https://leetcode.cn/problems/reformat-the-string/) | [LeetCode 题解链接](https://leetcode.cn/problems/reformat-the-string/solution/by-ac_oier-uk8z/) | 简单 | 🤩🤩🤩🤩 | diff --git "a/Index/\345\272\217\345\210\227 DP.md" "b/Index/\345\272\217\345\210\227 DP.md" index 7b0195ca..c4ba355a 100644 --- "a/Index/\345\272\217\345\210\227 DP.md" +++ "b/Index/\345\272\217\345\210\227 DP.md" @@ -17,6 +17,7 @@ | [926. 将字符串翻转到单调递增](https://leetcode.cn/problems/flip-string-to-monotone-increasing/) | [LeetCode 题解链接](https://leetcode.cn/problems/flip-string-to-monotone-increasing/solution/by-ac_oier-h0we/) | 中等 | 🤩🤩🤩🤩🤩 | | [978. 最长湍流子数组](https://leetcode-cn.com/problems/longest-turbulent-subarray/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-turbulent-subarray/solution/xiang-jie-dong-tai-gui-hua-ru-he-cai-dp-3spgj/) | 中等 | 🤩🤩🤩 | | [1035. 不相交的线](https://leetcode-cn.com/problems/uncrossed-lines/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/uncrossed-lines/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-bkaas/) | 中等 | 🤩🤩🤩🤩 | +| [1092. 最短公共超序列](https://leetcode.cn/problems/shortest-common-supersequence/) | [LeetCode 题解链接](https://leetcode.cn/problems/shortest-common-supersequence/solution/by-ac_oier-s346/) | 困难 | 🤩🤩🤩🤩 | | [1143. 最长公共子序列](https://leetcode-cn.com/problems/longest-common-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-common-subsequence/solution/gong-shui-san-xie-zui-chang-gong-gong-zi-xq0h/) | 中等 | 🤩🤩🤩🤩 | | [1218. 最长定差子序列](https://leetcode-cn.com/problems/longest-arithmetic-subsequence-of-given-difference/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-arithmetic-subsequence-of-given-difference/solution/gong-shui-san-xie-jie-he-tan-xin-de-zhua-dj1k/) | 中等 | 🤩🤩🤩🤩🤩 | | [1473. 粉刷房子 III](https://leetcode-cn.com/problems/paint-house-iii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/paint-house-iii/solution/gong-shui-san-xie-san-wei-dong-tai-gui-h-ud7m/) | 困难 | 🤩🤩🤩🤩 | diff --git "a/Index/\346\236\204\351\200\240.md" "b/Index/\346\236\204\351\200\240.md" index 84dc7ceb..c4ba5a07 100644 --- "a/Index/\346\236\204\351\200\240.md" +++ "b/Index/\346\236\204\351\200\240.md" @@ -8,6 +8,7 @@ | [899. 有序队列](https://leetcode.cn/problems/orderly-queue/) | [LeetCode 题解链接](https://leetcode.cn/problems/orderly-queue/solution/by-ac_oier-443m/) | 困难 | 🤩🤩🤩 | | [942. 增减字符串匹配](https://leetcode.cn/problems/di-string-match/) | [LeetCode 题解链接](https://leetcode.cn/problems/di-string-match/solution/by-ac_oier-pvjk/) | 简单 | 🤩🤩🤩🤩🤩 | | [961. 在长度 2N 的数组中找出重复 N 次的元素](https://leetcode.cn/problems/n-repeated-element-in-size-2n-array/) | [LeetCode 题解链接](https://leetcode.cn/problems/n-repeated-element-in-size-2n-array/solution/by-ac_oier-bslq/) | 简单 | 🤩🤩🤩🤩 | +| [1092. 最短公共超序列](https://leetcode.cn/problems/shortest-common-supersequence/) | [LeetCode 题解链接](https://leetcode.cn/problems/shortest-common-supersequence/solution/by-ac_oier-s346/) | 困难 | 🤩🤩🤩🤩 | | [1260. 二维网格迁移](https://leetcode.cn/problems/shift-2d-grid/) | [LeetCode 题解链接](https://leetcode.cn/problems/shift-2d-grid/solution/by-ac_oier-1blt/) | 简单 | 🤩🤩🤩🤩 | | [1537. 最大得分](https://leetcode.cn/problems/get-the-maximum-score/) | [LeetCode 题解链接](https://leetcode.cn/problems/get-the-maximum-score/solution/by-ac_oier-ht78/) | 困难 | 🤩🤩🤩🤩 | | [1719. 重构一棵树的方案数](https://leetcode-cn.com/problems/number-of-ways-to-reconstruct-a-tree/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-ways-to-reconstruct-a-tree/solution/gong-shui-san-xie-gou-zao-yan-zheng-he-f-q6fc/) | 困难 | 🤩🤩 | diff --git "a/LeetCode/1091-1100/1092. \346\234\200\347\237\255\345\205\254\345\205\261\350\266\205\345\272\217\345\210\227\357\274\210\345\233\260\351\232\276\357\274\211.md" "b/LeetCode/1091-1100/1092. \346\234\200\347\237\255\345\205\254\345\205\261\350\266\205\345\272\217\345\210\227\357\274\210\345\233\260\351\232\276\357\274\211.md" new file mode 100644 index 00000000..a675c0ad --- /dev/null +++ "b/LeetCode/1091-1100/1092. \346\234\200\347\237\255\345\205\254\345\205\261\350\266\205\345\272\217\345\210\227\357\274\210\345\233\260\351\232\276\357\274\211.md" @@ -0,0 +1,172 @@ +### 题目描述 + +这是 LeetCode 上的 **[1092. 最短公共超序列](https://leetcode.cn/problems/shortest-common-supersequence/solution/by-ac_oier-s346/)** ,难度为 **困难**。 + +Tag : 「序列 DP」、「LCS」、「最长上升子序列」、「动态规划」、「构造」、「双指针」 + + + +给出两个字符串 `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` 都由小写英文字母组成。 + +--- + +### LCS 求具体方案 + 构造 + +为了方便,我们令 `str1` 为 `s1`,`str2` 为 `s2`,并将两者长度记为 $n$ 和 $m$。 + +容易想到最终的方案必然是由三部分组成:两字符串的公共子序列(且必然是最长公共子序列)+ 两者特有的字符部分。 + +基于此,我们可以先使用对两者求 `LCS`,并在求具体方案时遵循:属于最长公共子序列的字符只添加一次,而特有于 `s1` 或 `s2` 的字符则独自添加一次。 + +求解 `LCS` 部分我们定义 **$f[i][j]$ 代表考虑 $s1$ 的前 $i$ 个字符、考虑 $s2$ 的前 $j$ 的字符,形成的最长公共子序列长度(为了方便,令下标从 $1$ 开始)。** + +当有了「状态定义」之后,基本上「转移方程」就是呼之欲出: + +* `s1[i]==s2[j]` : $f[i][j]=f[i-1][j-1]+1$。代表**必然使用 $s1[i]$ 与 $s2[j]$ 时** `LCS` 的长度。 +* `s1[i]!=s2[j]` : $f[i][j]=max(f[i-1][j], f[i][j-1])$。代表**必然不使用 $s1[i]$(但可能使用$s2[j]$)时** 和 **必然不使用 $s2[j]$(但可能使用$s1[i]$)时** `LCS` 的长度。 + +> **不了解 LCS 的同学可以看前置 🧀 : [LCS 模板题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247492097&idx=1&sn=f51f29d86df809d8ac43a40a1369b3d6)** + +当预处理出动规数组 `f` 之后,我们使用「双指针」和「通用 `DP` 求具体方案」的做法进行构造:使用 `i` 变量指向 `s1` 的尾部(即起始有 $i = n$),使用 `j` 变量指向 `s2` 的尾部(即起始有 $j = m$),根据 `i` 和 `j` 当前所在位置以及 $f[i][j]$ 从何值转移而来: + +1. 若 `i` 或 `j` 其一走完(`i = 0` 或 `j = 0`),将剩余字符追加到答案中; +2. 当 $f[i][j] = f[i - 1][j - 1] + 1$ 且 $s1[i] = s2[j]$ 时(可简化为 $s1[i] = s2[j]$ 判断),此时 `i` 指向的字符和 `j` 指向的字符为相同,且为 `LCS` 中的字符,将其追加到具体方案,并让 `i` 和 `j` 同时后移; +3. 当 $f[i][j] = f[i - 1][j]$,将 `s1[i]` 追加到答案中,令 `i` 后移; +4. 当 $f[i][j] = f[i][j - 1]$,将 `s2[j]` 追加到答案中,令 `j` 后移。 + +当条件 `3` 和 `4` 同时满足时,由于只需要输出任一具体方案,我们任取其一即可。 + +最后,由于我们是从后往前进行构造,在返回时需要再进行一次翻转。 + +Java 代码: +```Java +class Solution { + public String shortestCommonSupersequence(String str1, String str2) { + int n = str1.length(), m = str2.length(); + str1 = " " + str1; str2 = " " + str2; + char[] s1 = str1.toCharArray(), s2 = str2.toCharArray(); + int[][] f = new int[n + 10][m + 10]; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (s1[i] == s2[j]) f[i][j] = f[i - 1][j - 1] + 1; + else f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]); + } + } + StringBuilder sb = new StringBuilder(); + int i = n, j = m; + while (i > 0 || j > 0) { + if (i == 0) sb.append(s2[j--]); + else if (j == 0) sb.append(s1[i--]); + else { + if (s1[i] == s2[j]) { + sb.append(s1[i]); + i--; j--; + } else if (f[i][j] == f[i - 1][j]) { + sb.append(s1[i--]); + } else { + sb.append(s2[j--]); + } + } + } + return sb.reverse().toString(); + } +} +``` +TypeScript 代码: +```TypeScript +function shortestCommonSupersequence(s1: string, s2: string): string { + const n = s1.length, m = s2.length + s1 = " " + s1; s2 = " " + s2 + const f = new Array>() + for (let i = 0; i < n + 10; i++) f.push(new Array(m + 10).fill(0)) + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + if (s1[i] == s2[j]) f[i][j] = f[i - 1][j - 1] + 1 + else f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]) + } + } + let ans = "" + let i = n, j = m + while (i > 0 || j > 0) { + if (i == 0) ans += s2[j--] + else if (j == 0) ans += s1[i--] + else { + if (s1[i] == s2[j]) { + ans += s1[i] + i--; j-- + } else if (f[i][j] == f[i - 1][j]) { + ans += s1[i--] + } else { + ans += s2[j--] + } + } + } + return ans.split('').reverse().join('') +}; +``` +Python 代码: +```Python +class Solution: + def shortestCommonSupersequence(self, s1: str, s2: str) -> str: + n, m = len(s1), len(s2) + s1 = " " + s1 + s2 = " " + s2 + f = [[0] * (m + 10) for _ in range(n + 10)] + for i in range(1, n + 1): + for j in range(1, m + 1): + f[i][j] = f[i - 1][j - 1] + 1 if s1[i] == s2[j] else max(f[i - 1][j], f[i][j - 1]) + ans = "" + i, j = n, m + while i > 0 or j > 0: + if i == 0: + ans += s2[j] + j -= 1 + elif j == 0: + ans += s1[i] + i -= 1 + else: + if s1[i] == s2[j]: + ans += s1[i] + i -= 1 + j -= 1 + elif f[i][j] == f[i - 1][j]: + ans += s1[i] + i -= 1 + else: + ans += s2[j] + j -= 1 + return ans[::-1] +``` +* 时间复杂度:`LCS` 复杂度为 $O(n \times m)$;构造答案复杂度为 $O(n \times m)$。整体复杂度为 $O(n \times m)$ +* 空间复杂度:$O(n \times m)$ + +--- + +### 最后 + +这是我们「刷穿 LeetCode」系列文章的第 `No.1092` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 + +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 + +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 + +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 +