当前位置: 首页 > news >正文

代码随想录刷题笔记 Day 58 | 判断子序列 No.392 | 不同的子序列 No.115

文章目录

    • Day 58
      • 01. 判断子序列(No. 392)
        • <1> 题目
        • <2> 题解
        • <3> 代码
      • 02. 不同的子序列(No. 115)
        • <1> 题目
        • <2> 题解
        • <3> 代码

Day 58

01. 判断子序列(No. 392)

题目链接

代码随想录题解

<1> 题目

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

致谢:特别感谢 @pbrother 添加此问题并且创建所有测试用例。

示例 1:

输入:s = “abc”, t = “ahbgdc”
输出:true

示例 2:

输入:s = “axc”, t = “ahbgdc”
输出:false

提示:

  • 0 <= s.length <= 100
  • 0 <= t.length <= 10^4
  • 两个字符串都只由小写字符组成。
<2> 题解

如果仅考虑动态规划解法的话,本题是昨天的 最长公共子序列 的简化版本。

子序列的定义为:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

**s 是否是 t 的公共子序列,其实也就代表了 t 和 s 的最长公共子序列是否是 s。**所以本题的动态规划解法就是求得它们两个最长公共子序列的长度,然后判断长度是否等于 s 的长度即可;剩下的步骤和求最长公共子序列的方法就完全相同了。

1. 状态

既然是求最长的公共子序列,那就涉及到 长度,可以分解成如下的子问题:

s 的前一个元素和 t 第一个元素组成的最长公共子序列是多少?

s 的前两个元素和 t 第一个元素组成的最长公共子序列是多少?

s 的前三个元素和 t 第一个元素组成的最长公共子序列是多少?

。。。。。。

一直推到到 s 的前 s.length() 个元素和 t 的前 t.length() 个元素的最长公共子序列的长度为多少。

那这个状态能否转移呢?s 的前 m 个元素和 t 的前 n 个元素的公共子序列的长度,是可以通过 m - 1 和 n - 1 组成的公共子序列的长度推出的。

2. dp 数组的含义

dp 数组设定为一个二维数组 dp[i][j] 表示 s 的钱 i - 1 个元素和 t 的前 j - 1 个元素构成的最长公共子序列的大小。

使用这种后移一位的方式可以减少初始化的复杂程度。

3. 递推公式

对于 s 的第 i 个元素和 t 的第 j 个元素有两种情况:

  • 它们是相等的,所以它们可以作为构成子序列的元素存在
  • 它们不相等,在这个 长度范围 内需要被剔除

如果它们相等,那

dp[i][j] = dp[i - 1][j - 1] + 1

如果它们不相等,那就是延续前面的子序列在如下三个中取最大:

  • dp[i - 1][j]
  • dp[i - 1][j - 1]
  • dp[i][j - 1]

但因为第二种情况被包含了,所以仅对前两个情况取值即可,也就是

dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);

4. 初始化

本题中,下层的推导依赖的是左上角的值,所以只需要初始化第一行和第一列即可。

而本题的第一行代表 s 的第 0 个元素(从 1 开始,0 没有意义)和 t 的最长公共子序列,为 0

第一列代表着 t 的第 0 个元素和 s 的最长公共子序列,也是 0

所以均初始化为 0 即可,也就是不需要初始化。

<3> 代码
class Solution {public boolean isSubsequence(String s, String t) {int m = 0;int n = 0;while (m < s.length() && n < t.length()) {if (s.charAt(m) == t.charAt(n)) {m++;n++;} else {n++;}}return m == s.length();}
}

02. 不同的子序列(No. 115)

题目链接

代码随想录题解

<1> 题目

给你两个字符串 st ,统计并返回在 s子序列t 出现的个数,结果需要对 109 + 7 取模。

示例 1:

输入:s = “rabbbit”, t = “rabbit”
输出:3
解释:
如下所示, 有 3 种可以从 s 中得到 “rabbit” 的方案。
rabbbit
rabbbit
rabbbit

示例 2:

输入:s = “babgbag”, t = “bag”
输出:5
解释:
如下所示, 有 5 种可以从 s 中得到 “bag” 的方案。
babgbag
babgbag
babgbag
babgbag
babgbag

提示:

  • 1 <= s.length, t.length <= 1000
  • st 由英文字母组成
<2> 题解

1. 状态

本题虽然是困难题,但只要选好转移的状态,解答起来就比较简单了。

本题是要查询 s 中有多少种方法可以得到 t,比如第一个案例 s = “rabbbit”, t = “rabbit”,首先来考虑如何划分这个子问题:既然我去查询 s 有多少种方法可以得到 “rabbit” 比较困难,那能否先去找方法得到 “t” 呢?在之后能否得到 “it” 呢?

好,现在其实子问题的拆分就很明确了

  • s 中得到 “t” 有多少种方法?

  • s 中得到 “it” 有多少种方法?

  • s 中得到 “bit” 有多少种方法?

  • s 中得到 “bbit” 有多少种方法?

    。。。。。。

  • s 中得到 “rabbit” 有多少种方法?

这种考虑方式是从后往前去考虑的,其实也可以从前往后去转移。

那这个状态能否转移呢?

在这里插入图片描述

比如说现在要组成 BIT 这个序列,现在遍历到粉色的 B 了,现在,只需要知道,它后面的部分 能有几种方法组成 IT 就可以了。

2. dp 数组

本题需要一个二维的 dp 数组:

s = "rabbbit", t = "rabbit"

以上面的测试案例举例,dp[i][j] 表示目标字符串 t 从 i 到 末尾的字段在源字符串 s 从 j 到末尾的范围内有多少种构成的方式。

比如说 dp[3][0] 就是从 0 到 s.length - 1 这个范围内(“rabbbit”)构成 从 3 到 t.length - 1(“bit”)有几种方式。

3. 递推公式

还是举一个例子比较好理解:

比如说现在要计算这个节点,有多少种构成 BIT 的方式,就是要在这里寻找为 B 的节点

在这里插入图片描述

这时候它的前一层,也就是 dp[i + 1][j + 1] 表示从这个节点的后面开始,构成 IT 这个字段的方法

那这个节点构成 BIT 的方法就是 dp[i + 1][j + 1] 种嘛?是不对的,还需要再加上后一个节点构成 BIT 的方法,因为此时得到 BIT 的方法有如下两种:

在这里插入图片描述

所以最终得到的递推公式就是这样:

dp[i][j] = dp[i + 1][j + 1] + dp[i][j + 1];

搞清楚递推公式的含义非常关键。

那对于不相等的情况:

在这里插入图片描述

比如这里的节点 A,那此时从 A 到末尾构成 BIT 的方法就是它后面那个节点构成 BIT 的方法,得到:

dp[i][j] = dp[i][j + 1];

4. 初始化

因为本题的所有推导都是依赖第一行的,所以要先对第一行进行初始化

if (c1[c1.length - 1] == c2[c2.length - 1]) dp[c1.length - 1][c2.length - 1] = 1;for (int i = c2.length - 2; i >= 0; i--) {if (c2[i] == c1[c1.length - 1]) dp[c1.length - 1][i] = dp[c1.length - 1][i + 1] + 1;else dp[c1.length - 1][i] = dp[c1.length - 1][i + 1];}

先对最后一个元素进行特殊的处理,其他逻辑和前面的相同

<3> 代码
class Solution {public int numDistinct(String s, String t) {if (s.length() <= t.length()) {return s.equals(t) ? 1 : 0;}char[] c1 = t.toCharArray(); // 目标序列char[] c2 = s.toCharArray(); // 提供的序列int[][] dp = new int[c1.length][c2.length];if (c1[c1.length - 1] == c2[c2.length - 1]) dp[c1.length - 1][c2.length - 1] = 1;for (int i = c2.length - 2; i >= 0; i--) {if (c2[i] == c1[c1.length - 1]) dp[c1.length - 1][i] = dp[c1.length - 1][i + 1] + 1;else dp[c1.length - 1][i] = dp[c1.length - 1][i + 1];}for (int i = c1.length - 2; i >= 0; i--) {// 外层遍历要寻找的数组 c2for (int j = c2.length - 2; j >= 0; j--) {if (c1[i] == c2[j]) {dp[i][j] = dp[i + 1][j + 1] + dp[i][j + 1];} else {dp[i][j] = dp[i][j + 1];}}}return dp[0][0];}
}
http://www.lryc.cn/news/326095.html

相关文章:

  • 【C++11】thread线程库
  • 【OpenStack】创建系统(VM)实例镜像及实例创建方法
  • 灵途科技助力家电智能创新
  • Flask python :logging日志功能使用
  • ethers.js:sign(签名)
  • 使用npm i进行admin依赖安装的时候出现问题
  • 【Python笔记-FastAPI】定时任务实现(APScheduler)
  • 『Apisix入门篇』从零到一掌握Apache APISIX:架构解析与实战指南
  • easyExcel大数据量导出oom
  • react native上传二进制图片、视频的方法
  • JVM之堆
  • R语言实现——网状 Meta 分析
  • Java项目:77 springboot母婴商城
  • 【排序算法】深入解析快速排序(霍尔法三指针法挖坑法优化随机选key中位数法小区间法非递归版本)
  • 生成微信小程序二维码
  • 网络编程(1)写一个简单的UDP网络通信程序【回显服务器】,并且实现一个简单的翻译功能
  • Ansys Speos | Light Expert Group探测器组使用技巧
  • C#学习笔记3:Windows窗口计时器
  • C语言与sqlite3入门
  • Rancher(v2.6.3)——安装Rancher
  • Aapche Nutch建立自己的搜索引擎
  • 阅读笔记(ICIP2023)Rectangular-Output Image Stitching
  • 就业班 第二阶段 2401--3.26 day6 Shell初识 连接vscode
  • 碳课堂|什么是碳资产?企业如何进行碳资产管理?
  • 如何使用 ChatGPT 进行编码和编程
  • 学习java第二十四天
  • 中小型集群部署,Docker Swarm(集群)使用及部署应用介绍
  • gateway做负载均衡
  • pytorch中的torch.hub.load()
  • R语言学习——Rstudio软件