【1.1】动态规划求解不同的子序列

一、题目

给定一个字符串s和一个字符串t,计算在s的子序列中t出现的个数。
字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置
所组成的新字符串。(例如,"ACE"是"ABCDE"的一个子序列,而"AEC"不是)
题目数据保证答案符合32位带符号整数范围。

提示:
1)0<=s . length, t. length<=1000
2)s和t由英文字母组成

二、求解思路

动态规划解决思路

        s的子序列中出现t的个数,其实就是字符串s的所有子序列中,和字符串t完全一样的有多少个。
        我们定义dp[i][ j]表示t的前i个字符可以由s的前j个字符组成的个数(也可以说是字符串s 的前j个字符组成的子序列中,和字符串t 的前i个字符组成的字符串一样的有多少个)。
        那么最终我们只需要求出dp[tLength][ sLength]即可(其中tLength和sLength分别表示字符串t和s的长度)。
        如果字符串t的第i个字符和字符串s的第j个字符一样,如下所示

        如上图所示我们可以有两种选择。
        如果字符串t的第i个字符和字符串s的第j个字符不一样,也就是说字符串s的第j个字符不能匹配字符串t的第i个字符。

        那么我们只能计算字符串s的前j -1个字符构成的子序列中包含字符串t的前i个字符组成的字符串的个数。

        动态规划的三个步骤就是定义状态,列出递推公式,找出边界条件。

详细过程:

        我们可以定义二维数组 dp[i][j],其中 dp[i][j] 表示字符串 t 的前 i 个字符可以由字符串 s 的前 j 个字符组成的子序列的个数。这里,i 的取值范围是 0tLength(包括),j 的取值范围是 0sLength(包括),并且 tLengthsLength 分别是字符串 ts 的长度。

初始化时,我们有:

  • dp[0][j] = 1,对于所有 j(包括 j = 0),因为空字符串 ts 的任何子序列。
  • dp[i][0] = 0,对于所有 i > 0,因为 s 的空子序列不包含任何字符,所以无法与 t 的任何非空前缀匹配。

接下来,我们考虑 dp[i][j] 的状态转移方程。有两种情况:

  1. 如果 t[i-1] == s[j-1](注意字符串索引是从0开始的,所以我们用 i-1j-1 来访问实际字符),那么 dp[i][j] 可以由 dp[i-1][j-1](即 t 的前 i-1 个字符与 s 的前 j-1 个字符组成的子序列个数)加上 dp[i][j-1](即不选择 s[j-1] 时,t 的前 i 个字符可以由 s 的前 j-1 个字符组成的子序列个数)组成。这是因为我们可以选择将 s[j-1] 包含在当前子序列中(如果它与 t[i-1] 匹配),也可以选择不包含它。
  2. 如果 t[i-1] != s[j-1],那么 dp[i][j] 只能由 dp[i][j-1] 转移而来,因为我们不能选择 s[j-1] 来匹配 t[i-1]

用数学公式表示状态转移方程,我们有:

三、代码实现

C代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 函数 numDistinct 计算字符串 s 中包含字符串 t 作为子序列的不同子序列的数量。
int numDistinct(const char *s, const char *t) {// 获取两个字符串的长度int sLength = strlen(s);int tLength = strlen(t);// 为动态规划表分配内存空间int **dp = (int **)malloc((tLength + 1) * sizeof(int *));for (int i = 0; i <= tLength; i++) {dp[i] = (int *)malloc((sLength + 1) * sizeof(int));memset(dp[i], 0, (sLength + 1) * sizeof(int)); // 初始化为0}// 基础情况初始化,空字符串是任何字符串的子序列,所以初始化为 1for (int j = 0; j <= sLength; j++) {dp[0][j] = 1;}// 填充动态规划表for (int i = 1; i <= tLength; i++) {for (int j = 1; j <= sLength; j++) {// 如果 t 的第 i 个字符和 s 的第 j 个字符相同if (t[i - 1] == s[j - 1]) {// 有两种选择:// 1. 使用 s[j-1] 来匹配 t[i-1] (dp[i-1][j-1])// 2. 不使用 s[j-1] (dp[i][j-1])dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];} else {// 如果 t 的第 i 个字符和 s 的第 j 个字符不同// 只能选择不使用 s[j-1]dp[i][j] = dp[i][j - 1];}}}// 保存结果int result = dp[tLength][sLength];// 释放动态规划表的内存空间for (int i = 0; i <= tLength; i++) {free(dp[i]);}free(dp);// 返回 t 在 s 中作为子序列出现的总次数return result;
}int main() {const char *s = "rabbbit";const char *t = "rabbit";printf("The number of distinct subsequences is: %d\n", numDistinct(s, t));return 0;
}

在这段代码中:

  • 使用 mallocmemset 函数分配和初始化动态规划表 dp 的内存。
  • 动态规划表的行 dp[i] 表示字符串 t 的前 i 个字符在字符串 s 的前 j 个字符中作为子序列出现的次数。
  • 在计算完成后,使用 free 释放动态规划表 dp 的内存。
  • main 函数提供了一个简单的测试用例来演示函数的使用。

C++代码实现

#include <vector>
#include <string>// 函数 numDistinct 计算字符串 s 中包含字符串 t 作为子序列的不同子序列的数量。
int numDistinct(const std::string& s, const std::string& t) {// 获取两个字符串的长度int sLength = s.length();int tLength = t.length();// 使用 vector 构建二维动态规划表 dp// dp[i][j] 表示 t 的前 i 个字符可以在 s 的前 j 个字符中出现的次数std::vector<std::vector<int>> dp(tLength + 1, std::vector<int>(sLength + 1, 0));// 基础情况初始化,空字符串是任何字符串的子序列,所以初始化为 1for (int j = 0; j <= sLength; j++) {dp[0][j] = 1;}// 填充动态规划表for (int i = 1; i <= tLength; i++) {for (int j = 1; j <= sLength; j++) {// 如果 t 的第 i 个字符和 s 的第 j 个字符相同if (t[i - 1] == s[j - 1]) {// 有两种选择:// 1. 使用 s[j-1] 来匹配 t[i-1] (dp[i-1][j-1])// 2. 不使用 s[j-1] (dp[i][j-1])dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];} else {// 如果 t 的第 i 个字符和 s 的第 j 个字符不同// 只能选择不使用 s[j-1]dp[i][j] = dp[i][j - 1];}}}// 返回 t 在 s 中作为子序列出现的总次数return dp[tLength][sLength];
}

在这段代码中:

  • std::vector<std::vector<int>> dp 创建了一个二维动态数组来存储中间结果。
  • dp[i][j] 表示字符串 t 的前 i 个字符在字符串 s 的前 j 个字符中作为子序列出现的次数。
  • 初始化 dp[0][j]1,因为空字符串是任何字符串的子序列。
  • 循环遍历 ts,根据当前字符是否匹配,更新 dp 表。
  • 最终返回 dp[tLength][sLength],它包含了 t 作为 s 的子序列的出现次数。

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

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

相关文章

6.2、函数的定义

代码 #include <iostream> using namespace std; #include <string>//函数定义//语法&#xff1a;//返回值类型 函数名(参数列表) {函数体语句 return表达式}//加法函数 int add(int num1, int num2) {int sum num1 num2;return sum; } int main() {cout <&l…

聊聊Redis持久化策略RDB

写在文章开头 为避免服务器宕机着情况导致redis内存数据库数据丢失&#xff0c;redis默认出通过rdb保证可靠性&#xff0c;本文将从源码的角度带读者了解rdb读写时机和写入流程。 Hi&#xff0c;我是 sharkChili &#xff0c;是个不断在硬核技术上作死的 java coder &#xff…

刷代码随想录有感(124):动态规划——最长公共子序列

题干&#xff1a; 代码&#xff1a; class Solution { public:int findLength(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>>dp(nums1.size() 1, vector<int>(nums2.size() 1, 0));int res 0;for(int i 1; i <…

数据集采样策略对模型性能的影响问题

数据集采样策略对模型性能的影响问题&#xff0c;需要具体代码示例 随着机器学习和深度学习的快速发展&#xff0c;数据集的质量和规模对于模型性能的影响变得越来越重要。在实际应用中&#xff0c;我们往往面临着数据集规模过大、样本类别不平衡、样本噪声等问题。这时&#…

lnternet 发展史

一&#xff0c;lnternet 发展史 ARPA net &#xff08;上世纪50年代二战结束&#xff09; 无线 战场指挥通信协议落后 TCP/IP 包交换 WEB (70年代 ) 80年代 90年代 二&#xff0c;互联网的典型应用&#xff1a; 96年到2008年 第一代技术…

AJAX的概述 ,同步和异步的区别 ,AJAX 的交互模型和传统交互模型的区别

一. AJAX的概述 1.1 什么是ajax 同步&#xff1a; 异步&#xff1a; 1.AJAX Asynchronous JavaScript and XML&#xff08;异步的 JavaScript 和 XML&#xff09;。 ​ 说明&#xff1a;异步&#xff1a;就是不同步。例如我们向后台发送请求&#xff0c;同步的方式是后台必…

svn忽略上传文件node_modules文件

文章目录 1.点击svn项目右键-》选中svn的属性2. 点击 新建3. 点击其他4. 选择属性 svn:global-ignores5. 输入忽略文件 1.点击svn项目右键-》选中svn的属性 2. 点击 新建 3. 点击其他 4. 选择属性 svn:global-ignores 5. 输入忽略文件

四、【源码】Bean属性注入

源码地址&#xff1a;https://github.com/spring-projects/spring-framework 仓库地址&#xff1a;https://gitcode.net/qq_42665745/spring/-/tree/04-porperty-inject Bean属性注入 属性注入相关的类 1.PropertyValue&#xff1a;属性对象&#xff0c;name:value 2.Prope…

数据结构 —— 二叉树

1.树的概念及结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;它有着多分支&#xff0c;层次性的特点。 由于其形态类似于自然界中倒过来的数&#xff0c;所以我们将这种数据结构称为“树形结构” 注意&#xff1a; 树形结构中&#xff0c;子树之间不能有交集&#x…

降重工具大揭秘:AI如何帮你轻松搞定论文重写?

已经天临五年了&#xff0c;大学生们还在为论文降重烦恼……手动降重确实是个难题&#xff0c;必须要先付点小经费去靠谱的网站查重&#xff0c;再对着红字标注去改&#xff0c;后面每一次的论文呢查重结果都像赌//博&#xff0c;谁也不知道明明是同一篇文章&#xff0c;第二次…

2024鲲鹏昇腾创新大赛集训营Ascend C算子学习笔记

异构计算架构&#xff08;CANN&#xff09; 对标英伟达的CUDA CuDNN的核心软件层&#xff0c;向上支持多种AI框架&#xff0c;向下服务AI处理器&#xff0c;发挥承上启下的关键作用&#xff0c;是提升昇腾AI处理器计算效率的关键平台。主要包括有各种引擎、编译器、执行器、算…

(番外篇)指针的一些相关习题讲解(速进,干货满满)(2)

前言&#xff1a; 小编感觉最近有点太堕落&#xff0c;于是我开始从事这篇文章的撰写&#xff0c;现在也是进入七月份了&#xff0c;我现在文章开头定一个小目标&#xff0c;我决定在七月份发布至少十篇文章&#xff0c;希望我可以说到做到&#xff08;我前面就口头欠了不少文章…

OpenSSL的一些使用案例

目录 一、介绍 二、基本使用 1、Shell &#xff08;1&#xff09;文件加解密 &#xff08;2&#xff09;生成密钥文件 2、API &#xff08;1&#xff09;md5sum &#xff08;2&#xff09;AES256加解密 一、介绍 本篇博客重点不是详细描述 OpenSSL 的用法&#xff0c;只…

什么是校园气象站

在科技日新月异的今天&#xff0c;气象观测不仅局限于专业的气象机构&#xff0c;它已经走进了我们的校园&#xff0c;成为了学生们探索自然、学习科学知识的重要平台。 校园气象站是设置在学校内部&#xff0c;用于进行气象观测、数据记录和科学实验的设施。它通常由气象传感器…

常见锁策略之可重入锁VS不可重入锁

可重入锁VS不可重入锁 有一个线程,针对同一把锁,连续加锁两次,如果产生了死锁,那就是不可重入锁,如果没有产生死锁,那就是可重入锁. 死锁 我们之前引入多线程的时候不是讲了一个加数字的案例么,我们今天以它来举例 当我们这样写的时候会出现什么问题? 分析:第一个synchron…

前端基础--Vue3

Vue3基础 VUE3和VUE2的区别 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece 于 2022 年 2 月 7 日星期一成为新的默认版本! Vue3性能更高,初次渲染快55%, 更新渲染快133% 。体积更小 Vue3.0 打包大小减少41%。 同时Vue3可以更好的支持T…

基于微服务智能推荐健康生活交流平台的设计与实现(SpringCloud SpringBoot)+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

解决使用monaco-editor编译器,编译器展示内容没有超过编译器高度,但是出现滚动条问题

前言&#xff1a; 最近在完成项目时&#xff0c;有使用编译器进行在线编辑的功能&#xff0c;就选用了monaco-editor编译器&#xff0c;但是实现功能之后&#xff0c;发现即使在编译器展示的内容没有超过编译器高度的情况下&#xff0c;编译器依旧存在滚动条&#xff0c;会展示…

计算机网络--网络层

一、网络层的服务和功能 网络层主要为应用层提供端对端的数据传输服务 网络层接受运输层的报文段&#xff0c;添加自己的首部&#xff0c;形成网络层分组。分组是网络层的传输单元。网络层分组在各个站点的网络层之间传输&#xff0c;最终到达接收方的网络层。接收方网络层将运…

如何在 Java 应用中使用 Jedis 客户端库来实现 Redis 缓存的基本操作

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…