算法训练营 day58 动态规划 判断子序列 不同的子序列

news2025/4/17 3:48:22

算法训练营 day58 动态规划 判断子序列 不同的子序列

判断子序列

392. 判断子序列 - 力扣(LeetCode)

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

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

进阶:

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

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

    dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]

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

  2. 确定递推公式

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

  • 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];

  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]同理

  1. 确定遍历顺序

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

如图所示:

在这里插入图片描述

  1. 举例推导dp数组

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

在这里插入图片描述

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 boolean isSubsequence(String s, String t) {
        int[][] dp = new int[s.length()+1][t.length()+1];
        char[] number1 = s.toCharArray();
        char[] number2 = t.toCharArray();

        for (int i = 1; i <= number1.length; i++) {
            for (int j = 1; j <=number2.length; j++) {
                if (number1[i-1]==number2[j-1]) dp[i][j] = dp[i-1][j-1]+1;
                else dp[i][j] = dp[i][j-1];
                System.out.print(dp[i][j]);
            }
            System.out.println();
        }
        return dp[number1.length][number2.length]==s.length();
    }
}

不同的子序列

115. 不同的子序列 - 力扣(LeetCode)

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)

题目数据保证答案符合 32 位带符号整数范围。

  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]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。

  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]可以根据之前计算出来的数值进行计算。

  1. 举例推导dp数组

以s:“baegg”,t:"bag"为例,推导dp数组状态如下:

在这里插入图片描述

如果写出来的代码怎么改都通过不了,不妨把dp数组打印出来,看一看,是不是这样的。

class Solution {
    public int numDistinct(String s, String t) {
        char[] number1 = s.toCharArray();
        char[] number2 = t.toCharArray();
        int[][] dp = new int[number1.length+1][number2.length+1];
        for (int i = 0; i <=number1.length; i++) {
            dp[i][0]=1;
        }
        for (int i = 1; i <=number1.length ; i++) {
            for (int j = 1; j <=number2.length ; j++) {
                if (number1[i-1]==number2[j-1]) 
                    dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
                else dp[i][j] = dp[i-1][j];
            }
        }
        return dp[number1.length][number2.length];
    }
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

03、SVN 建立版本库

SVN 建立版本库1 版本库2 版本库的建立步骤2.1 创建版本库的根目录2.2 创建子目录2.3 通过命令创建版本库2.4 生成目的介绍1 版本库 Subversion 是将文件数据信息保存到版本库中进行管理的Subversion 允许用户对版本库目录进行定制 2 版本库的建立步骤 2.1 创建版本库的根目…

引用数据类型和基本数据类型

1.基本数据类型&#xff1a; byte&#xff1a;字节类型&#xff0c;Java中最小的数据类型&#xff0c;1个字节&#xff0c;取值范围-128~127&#xff0c;默认值0 char&#xff1a;字符型&#xff0c;用于存储单个字符&#xff0c;2个字节&#xff0c;取值范围0~65535&#xf…

Redis使用,AOF、RDB

前言 如果有人问你&#xff1a;"你会把 Redis 用在什么业务场景下&#xff1f;" 我想你大概率会说&#xff1a;"我会把它当作缓存使用&#xff0c;因为它把后端数据库中的数据存储在内存中&#xff0c;然后直接从内存中读取数据&#xff0c;响应速度会非常快。…

“速通“ 老生常谈的HashMap [实现原理源码解读]

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 HashMap 实现原理&&源码解读 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f…

RocketMQ动态增加NameServer

前言 通过HTTP服务来设置NameServer地址&#xff0c;是唯一支持动态增加NameServer的方式&#xff0c;无需重启其它组件 网上搜了下没看到有兄弟们演示这块&#xff0c;所以刚才自己试了试&#xff0c;做个笔记 本文有详细的演示过程 都知道NameServer有4种配置方式&#xff0…

simulink入门指南

系列文章目录 文章目录系列文章目录常用操作技巧项目练习 - 动态系统二阶系统建模比例积分控制燕子俯冲系统建模滤波位操作总结常用操作技巧 画面缩放 空格键 鼠标左键: 拖拽空格键: fit屏幕 信号操作 双击signal可添加标签, 鼠标右键拖拽可给信号添加分支同一分支上的sig…

【并发基础】线程,进程,协程的详细解释

目录 一、什么是进程和线程 1.1 进程是什么呢&#xff1f; 1.2 线程又是什么呢&#xff1f; 1.3 线程和进程之间的关系 操作系统、进程、线程之间的关系图&#xff1a; 进程与线程的模型图&#xff1a; 下面来思考这样一个问题&#xff1a;为什么程序计数器、虚拟机栈和本地方法…

QNX7.1 交叉编译开源库

1.下载QNX7.1 SDK并解压 ITL:~/work/tiqnx710$ ls -l 总用量 16 drwxrwxr-x 4 xxx4096 1月 28 13:38 host -rwxrwxr-x 1 xxx 972 1月 28 13:38 qnxsdp-env.bat -rwxrwxr-x 1 xxx 1676 1月 28 13:38 qnxsdp-env.sh drwxrwxr-x 3 xxx 4096 1月 28 13:38 target xxxITL:~/work/ti…

NetApp SnapCenter 备份管理 ——借助应用程序一致的数据备份管理,简化混合云操作

NetApp SnapCenter 简单、可扩展、赋权&#xff1a;跨 Data Fabric 的企业级数据保护和克隆管理 主要优势 • 利用与应用程序集成的工作流和预定义策略简化备份、恢复和克隆管理。 • 借助基于存储的数据管理功能提高性能和可用性&#xff0c;并缩短测试和开发用时。 • 提供基…

干货 | 浅谈机器人强化学习--从仿真到真机迁移

“对于机器人的运动控制&#xff0c;强化学习是广受关注的方法。本期技术干货&#xff0c;我们邀请到了小米工程师——刘天林&#xff0c;为大家介绍机器人&#xff08;以足式机器人为主&#xff09;强化学习中的sim-to-real问题及一些主流方法。”一、前言设计并制造可以灵活运…

【靶机】vulnhub靶机cybox1

Vulnhub靶机Cybox下载地址 Cybox: 1.1 ~ VulnHub 信息搜集 首先进行靶机ip的发现。 sudo arp-scan -l 接着使用nmap扫描开放的端口&#xff0c;这里使用扫描全部端口和详细信息太慢了&#xff0c;分开检测 命令&#xff1a;nmap -p 21,25,80,110,143,443 -sV -A 192.168.174…

CentOS救援模式(Rescue Mode)及紧急模式(Emergency Mode)

当CentOS操作系统崩溃&#xff0c;无法正常启动时&#xff0c;可以通过救援模式或者紧急模式进行系统登录。启动CentOS, 当出现下面界面时&#xff0c;按e进入编辑界面。在编辑界面里&#xff0c;加入参数&#xff1a;systemd.unitrescue.target &#xff0c;然后Ctrl-X启动进入…

Linux服务器上传文件到阿里云oss对象存储的两种方法ossutil、curl

ossutil支持在Windows、Linux、macOS等系统中运行&#xff0c;您可以根据实际环境下载和安装合适的版本。安装过程中&#xff0c;需要使用解压工具&#xff08;unzip、7z&#xff09;解压软件包&#xff0c;请提前安装其中的一个解压工具。yum -y install unzipLinux系统一键安…

正点原子第一期

ZYNQ是一个fpga用来硬件编程&#xff0c;外加一个软件编程 FPGA是可通过编程来修改其逻辑功能的数字集成电路 第三篇语法篇 第七章 verilog HDL语法 Verilog的简介 可编程逻辑电路&#xff1a;允许用户自行修改内部连接的集成电路&#xff0c;其内部的电路结构可以通过编程数…

ADAS-CIS相机关键参数综述

引言 “ CIS传感器关键参数是工程师选型参考的依据&#xff0c;抛开镜头&#xff0c;本文介绍CIS可见光传感器关键参数之CRA、QE、CFA、Shutter、SNR等。” 关键参数 CRA CFA(Chief Ray Angle)主光角&#xff0c;在上一篇可见光相机的介绍中我们介绍了Sensor的叠层结构&am…

leecode+剑指offer

1.算法入门14天 1.704二分查找&#xff1a; 题目描述&#xff1a;给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 思路…

Matlab进阶绘图第3期—方块热图

方块热图是一种特殊的热图&#xff08;Heatmap&#xff09;。 与传统热图相比&#xff0c;方块热图通过不同颜色、不同大小的正方形以表示数据/特征的大小&#xff0c;能够更加直观地对数据分布情况进行分析&#xff0c;也因此可以在一些顶级期刊中看到它的身影&#xff0c;比…

数据库设计三大范式

数据库设计遵循三大范式的理由&#xff1a;在面对复杂是数据库设计的时候&#xff0c;设计数据库要遵循一定的规则&#xff0c;有了一定的规范&#xff0c;这样就可以是自己看起来舒服。 1&#xff0e;第一范式(确保每列保持原子性) 第一范式主要是保证数据表中的每一个字段的…

Java缓存面试题——Redis解决方案

文章目录1、什么是缓存击穿&#xff1f;该如何解决2、什么是缓存穿透&#xff1f;该如何解决3、什么是缓存雪崩&#xff1f;该如何解决4、什么是BigKey&#xff1f;该如何解决bigkey的危害发现bigkey解决bigkey5、redis过期策略都有哪些&#xff1f;6、讲一讲Redis缓存的数据一…

FL Studio 21 中文正式版发布支持多种超个性化主题

万众期待的 FL Studio 21 版本正式发布上线&#xff0c;目前在紧锣密鼓的安排上线中&#xff0c;届时所有购买正版 FL Studio 的用户&#xff0c;都可以免费升级到21版&#xff01; 按照惯例&#xff0c;本次新版也会增加全新插件&#xff0c;来帮助大家更好地创作。今天先给大…