刷题DAY55 | LeetCode 392-判断子序列 115-不同的子序列

392 判断子序列(easy)

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

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

思路:动态规划法,这道题应该算是编辑距离的入门题目,从题意中可以发现,只需要计算删除的情况,不用考虑增加和替换的情况。

动态规划五部曲分析如下:

  1. 确定dp数组(dp table)以及下标的含义
  • dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。

注意这里是判断s是否为t的子序列。即t的长度是大于等于s的。

为了初始化方便统一以下标i-1为结尾的字符串来计算,这样在下面的递归公式中会容易理解一些。

  1. 确定递推公式

在确定递推公式的时候,首先要考虑如下两种操作,整理如下:

if (s[i - 1] == t[j - 1])
t中找到了一个字符在s中也出现了
if (s[i - 1] != t[j - 1])
相当于t要删除元素,继续匹配

  • if (s[i - 1] == t[j - 1]),那么dp[i][j] = dp[i - 1][j - 1] + 1;,因为找到了一个相同的字符,相同子序列长度自然要在dp[i-1][j-1]的基础上加1(如果不理解,在回看一下dp[i][j]的定义)

  • if (s[i - 1] != t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1];

其实这里 大家可以发现和1143.最长公共子序列的递推公式基本那就是一样的,区别就是 本题 如果删元素一定是字符串t,而 1143.最长公共子序列 是两个字符串都可以删元素。

  1. dp数组如何初始化

从递推公式可以看出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1],所以dp[0][0]和dp[i][0]是一定要初始化的。

这里大家已经可以发现,在定义dp[i][j]含义的时候为什么要表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。

因为这样的定义在dp二维矩阵中可以留出初始化的区间,如图:
在这里插入图片描述
如果要是定义的dp[i][j]是以下标i为结尾的字符串s和以下标j为结尾的字符串t,初始化就比较麻烦了。

dp[i][0] 表示以下标i-1为结尾的字符串,与空字符串的相同子序列长度,所以为0. dp[0][j]同理。

vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
  1. 确定遍历顺序

同理从递推公式可以看出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1],那么遍历顺序也应该是从上到下,从左到右

如图所示:

在这里插入图片描述

  1. 举例推导dp数组

以示例一为例,输入:s = “abc”, t = “ahbgdc”,dp状态转移图如下:

392.判断子序列2

dp[i][j]表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t 相同子序列的长度,所以如果dp[s.size()][t.size()] 与 字符串s的长度相同说明:s与t的最长相同子序列就是s,那么s 就是 t 的子序列。

图中dp[s.size()][t.size()] = 3, 而s.size() 也为3。所以s是t 的子序列,返回true。

代码实现:

class Solution {
public:bool isSubsequence(string s, string t) {vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));for (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = dp[i][j - 1];}}if (dp[s.size()][t.size()] == s.size()) return true;return false;}
};
  • 时间复杂度:O(n × m)
  • 空间复杂度:O(n × m)

详细解析:
思路视频
代码实现文章


115 不同的子序列(hard)

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

思路:动态规划法,本题的关键是递推公式的思考

  1. 确定dp数组(dp table)以及下标的含义

dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。

  1. 确定递推公式

这一类问题,基本是要分析两种情况

  • s[i - 1] 与 t[j - 1]相等
  • s[i - 1] 与 t[j - 1] 不相等

当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]。一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。

为什么还要考虑 不用s[i - 1]来匹配,都相同了指定要匹配啊。例如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。

所以当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];

当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j]所以递推公式为:dp[i][j] = dp[i - 1][j];

  1. dp数组如何初始化

从递推公式dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; 和 dp[i][j] = dp[i - 1][j]; 中可以看出dp[i][j] 是从上方和左上方推导而来,如图:,那么 dp[i][0] 和dp[0][j]是一定要初始化的。

每次当初始化的时候,都要回顾一下dp[i][j]的定义,不要凭感觉初始化。

dp[i][0]表示什么呢?

dp[i][0] 表示:以i-1为结尾的s可以随便删除元素,出现空字符串的个数。

那么dp[i][0]一定都是1,因为也就是把以i-1为结尾的s,删除所有元素,出现空字符串的个数就是1。

再来看dp[0][j],dp[0][j]:空字符串s可以随便删除元素,出现以j-1为结尾的字符串t的个数。

那么dp[0][j]一定都是0,s如论如何也变成不了t。

最后就要看一个特殊位置了,即:dp[0][0] 应该是多少。

dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串t。

初始化分析完毕,代码如下:

vector<vector<long long>> dp(s.size() + 1, vector<long long>(t.size() + 1));
for (int i = 0; i <= s.size(); i++) dp[i][0] = 1;
for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和dp数组初始化的时候放在一起,但我为了凸显初始化的逻辑,所以还是加上了。
  1. 确定遍历顺序

从递推公式dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; 和 dp[i][j] = dp[i - 1][j]; 中可以看出dp[i][j]都是根据左上方和正上方推出来的。

所以遍历的时候一定是从上到下,从左到右,这样保证dp[i][j]可以根据之前计算出来的数值进行计算。

代码实现:

class Solution {
public:int numDistinct(string s, string t) {vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1));for (int i = 0; i < s.size(); ++i) dp[i][0] = 1;for (int i = 1; i <= s.size(); ++i) {for (int j = 1; j <= t.size(); ++j) {if (s[i - 1] == t[j - 1]) dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % 1000000007;else dp[i][j] = dp[i - 1][j];}}return dp[s.size()][t.size()] ;}
};
  • 时间复杂度: O(n * m)
  • 空间复杂度: O(n * m)

详细解析:
思路视频
代码实现文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/623077.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024蓝桥杯——宝石问题

先展示题目 声明 以下代码仅是我的个人看法&#xff0c;在自己考试过程中的优化版&#xff0c;本人考试就踩了很多坑&#xff0c;我会—一列举出来。代码可能很多&#xff0c;但是总体时间复杂度不高只有0(N) 函数里面的动态数组我没有写开辟判断和free&#xff0c;这里我忽略…

janus部署

配置和运行janus 1. 配置nginx 安装nginx&#xff0c;主要用来提供web访问。 生成证书 mkdir -p ~/cert cd ~/cert # CA私钥 openssl genrsa -out key.pem 2048 # 自签名证书 openssl req -new -x509 -key key.pem -out cert.pem -days 1095安装nginx #下载nginx 1.15.8版…

Linux应用开发笔记(五)网络编程(二)多线程编程

文章目录 前言一、线程和进程1. 进程&#xff08;Process&#xff09;2. 线程&#xff08;Thread&#xff09;3. 二者的比较 二、多线程和多进程三. 代码编写1. 相关函数pthread_create( )函数pthread_exit( )函数pthread_join( )函数 2. 线程同步3. 互斥量4. 条件变量5. 实验代…

自然语言处理——情绪检测数据集

一、重要性及意义 情绪检测的重要性和意义体现在多个方面&#xff0c;不仅对于个人日常生活有深远影响&#xff0c;也在多个行业和领域中扮演着关键角色。以下是情绪检测的重要性和意义的具体体现&#xff1a; 提高人机交互体验&#xff1a; 在人工智能和机器学习驱动的系统中…

企业图纸管理软件,企业图纸管理软件有哪些推荐?

企业图纸管理软件是一种专门用于组织、存储、管理和共享企业图纸及相关文档的工具。这类软件可以帮助企业实现图纸的集中化、规范化管理&#xff0c;提高工作效率&#xff0c;降低管理成本。 企业图纸管理软件的核心功能通常包括以下几个方面&#xff1a; 集中化管理&#xff1…

基于SpringBoot+Vue的卓越导师双选系统设计与实现(源码+文档+包运行)

一.系统概述 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;因此传统管理方式就不适合。为了让导师选择信息的管理模式进行升级&#xff0c;也为了更好的维护导师选择信息&#xff0c;卓越导师双选系统的开发运用就显得很有必要。…

基于LNMP部署wordpress

目录 一.环境准备 二.配置源并安装 三.配置Nginx 四.配置数据库 五.上传源码并替换 六.打开浏览器&#xff0c;输入虚拟机ip访问安装部署 七.扩展增加主题 一.环境准备 centos7虚拟机 关闭防火墙和seliunx stop firewalld #关闭防火墙 setenforce 0 …

记录一次内存溢出

1、查看catalina相关日志&#xff0c;确定关键字相关行号 文件&#xff1a;catalina.out命令1&#xff1a;cat -n catalina.out |grep -a OutOfMemoryError与内存溢出相关的如上&#xff0c;每一个行号其实都对应到具体时间点。可以发现&#xff0c;这个范围相符合&#xff1…

temux安装debian自用记录

http://ip:9001/ user/123 http://ip:5705/index admin/drpy 一、安装Ubuntu1804 1&#xff0e;首先安装termux.app 2&#xff0e;启动该app&#xff0c;输入命令 curl -Lo l l.tmoe.me; sh l 3&#xff0e;运行过程中连续选“Y”&…

【学习笔记十三】EWM常见上架策略介绍

一、手工维护上架策略 系统不确定Storage type 和 bin&#xff0c;需要在创建仓库任务时或者确认仓库任务时手工输入仓位 1.后台配置-定义存储类型的类型0010 ①存储行为&#xff1a;标准仓位 ②入库规则&#xff1a;空仓未或添加至现有库存/空仓位 ③通用仓库任务&#x…

【学习笔记十二】EWM上架仓位确定逻辑及操作演示

一、前言 关于EWM上架仓位确定的过程&#xff0c;我在【学习笔记十一】EWM上架目标仓位确定过程及配置-CSDN博客中讲到了 EWM根据仓库类型&#xff08;storage type&#xff09;、仓库分区&#xff08;storage section&#xff09;和上架策略&#xff08;putaway strategies&…

WordPress网站上添加看板娘

续接上篇——基于LNMP部署wordpress-CSDN博客 目录 一.下载并解压 二.设置头文件 修改header.php 修改配置文件footer.php 三.将你设置的主题包上传到/usr/share/nginx/html/wp-content这个目录里 四.扩展——将看板娘修改到左侧 一.下载并解压 [rootaliyun ~]# wget htt…