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

算法日记day 43(动归之不同的子序列|两个字符串的删除操作)

一、不同的子序列

题目:

给你两个字符串 s 和 t ,统计并返回在 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

思路:

本题dp数组的含义是以i-1为结尾的字符串s和以j-1为结尾的字符串t的可构成t的情况有dp[i][j]种,如果有相对应的元素,即为s[i-1]=t[i-1],此时应有两种情况

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

前面的dp[i-1][j-1]是元素都匹配的情况,后面的dp[i-1][j]是模拟删除一种元素,即不匹配时计算s的前i-1个元素和 t 前 j 个元素

例如  s="bagg"

          t="bag" 例子中存在有s[2] = t[2],但是也存在s[3] = t[2],两种均需考虑

相对应的如果不存在对应元素的匹配,即s[i-1]不等于t[i-1],此时只能从上一层继承,即为

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

代码:

public int numDistinct(String s, String t) {// 计算字符串 s 和 t 的长度,并在它们的长度上各加 1int n = s.length() + 1;int m = t.length() + 1;// 将字符串 s 和 t 转换为字符数组,以便在后续操作中更高效地访问单个字符char[] s1 = s.toCharArray();char[] t1 = t.toCharArray();// 创建一个二维数组 dp,用于存储子问题的解// dp[i][j] 表示 s 的前 i 个字符中,t 的前 j 个字符出现的子序列的数量int[][] dp = new int[n][m];// 初始化 dp 表的第一列,表示 t 是空字符串时的情况// 任何 s 的前 i 个字符中,空字符串作为子序列的数量总是 1for (int i = 0; i < s.length() + 1; i++) {dp[i][0] = 1;}// 填充 dp 表的其他部分for (int i = 1; i < n; i++) {for (int j = 1; j < m; j++) {if (s1[i - 1] == t1[j - 1]) {// 当前字符匹配// dp[i][j] 是由两个部分组成:// 1. dp[i - 1][j - 1]:当前字符匹配,计算 s 的前 i-1 个字符和 t 的前 j-1 个字符// 2. dp[i - 1][j]:当前字符不匹配,计算 s 的前 i-1 个字符和 t 的前 j 个字符dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];} else {// 当前字符不匹配// dp[i][j] 只能从 dp[i - 1][j] 继承,即当前字符在 s 中不考虑dp[i][j] = dp[i - 1][j];}}}// 返回 dp 表的右下角的值,即整个字符串 s 中 t 作为子序列的数量return dp[s.length()][t.length()];
}

 

二、两个字符串的删除操作

题目:

给定两个单词 word1 和 word2 ,返回使得 word1 和  word2 相同所需的最小步数

每步 可以删除任意一个字符串中的一个字符。

示例 1:

输入: word1 = "sea", word2 = "eat"
输出: 2
解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"

示例  2:

输入:word1 = "leetcode", word2 = "etco"
输出:4

思路:

dp数组的含义为以i-1为结尾的字符串word1和以j-1为结尾的字符串word2使其两字符串相等的操作需要dp[i][j]步,共有三种情况

第一种:删除字符,删除其中的一个元素,操作数加一

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

第二种:添加字符,加入一个字符,操作数加一 

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

第三种:替换字符,相当于先删除再加入,操作数加二

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

在初始化dp数组时,由于其含义,对边界初始化分别应是

                                                   dp[i][0] = i,dp[0][j] = j 

代码:

public int minDistance(String word1, String word2) {// 创建 dp 表,dp[i][j] 代表将 word1 的前 i 个字符转换为 word2 的前 j 个字符的最小编辑距离int[][] dp = new int[word1.length() + 1][word2.length() + 1];// 初始化第一列,表示将 word1 的前 i 个字符变为空字符串所需的删除操作数for (int i = 0; i < word1.length() + 1; i++)dp[i][0] = i;// 初始化第一行,表示将空字符串变为 word2 的前 j 个字符所需的插入操作数for (int j = 0; j < word2.length() + 1; j++)dp[0][j] = j;// 填充 dp 表,计算最小编辑距离for (int i = 1; i < word1.length() + 1; i++) {for (int j = 1; j < word2.length() + 1; j++) {// 如果当前字符相同,无需额外操作,继承上一个状态if (word1.charAt(i - 1) == word2.charAt(j - 1)) {dp[i][j] = dp[i - 1][j - 1];} else {// 计算字符不同的情况下的最小操作数// 替换字符:dp[i - 1][j - 1] + 2// 删除字符:dp[i - 1][j] + 1// 插入字符:dp[i][j - 1] + 1dp[i][j] = Math.min(dp[i - 1][j - 1] + 2, Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));}}}// 返回 dp 表中最后一个元素的值,即 word1 转换为 word2 的最小编辑距离return dp[word1.length()][word2.length()];
}

 另一种思路:

由于最后求得的字符串,相当于是求得了两个字符的最大公共子序列,只需要在最大公共子序列的思路上返回初始的两数组长度之和减去最长公共子序列长度的两倍,即为需要操作的次数

代码:

public int minDistance(String word1, String word2) {int len1 = word1.length();  // 获取 word1 的长度int len2 = word2.length();  // 获取 word2 的长度// 创建一个二维数组 dp,其中 dp[i][j] 表示 word1 的前 i 个字符和 word2 的前 j 个字符的最长公共子序列的长度int[][] dp = new int[len1 + 1][len2 + 1];// 填充 dp 表for (int i = 1; i <= len1; i++) {for (int j = 1; j <= len2; j++) {// 如果当前字符相同,最长公共子序列的长度增加 1if (word1.charAt(i - 1) == word2.charAt(j - 1)) {dp[i][j] = dp[i - 1][j - 1] + 1;} else {// 如果字符不同,最长公共子序列的长度是两个子问题中较大的那个dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);}}}// dp[len1][len2] 是 word1 和 word2 的最长公共子序列的长度// 编辑距离等于 word1 和 word2 的总长度减去 2 倍的最长公共子序列的长度// 这个公式的意思是:从两个字符串中删除掉不在最长公共子序列中的字符,得到的字符数之和就是最小编辑距离return len1 + len2 - dp[len1][len2] * 2;
}

今天的学习就到这里 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 一文学会用 Maven
  • java JVM ZGC垃圾收集器关键特性和工作原理
  • 奇异递归Template有啥奇的?
  • Linux CentOS java JDK17
  • Python数据分析:Pandas与NumPy结合,实现高效数值计算,提升数据分析效率的最佳实践
  • Ubuntu10.04安装skyeye1.3.2问题
  • 如何使⽤组将⼀个文件拆分成多个文件 (LINQ)(C#)
  • Milvus向量数据库-内存中索引简介
  • 动手学深度学习7.5 批量规范化-笔记练习(PyTorch)
  • 将所有PPT中的字体颜色白色改成黑色---使用AI提高效率
  • Java-RestTemplate工具类
  • P1379 八数码难题 绿
  • PlayCanvas的EventHandler.on函数修改了返回值导致链式调用无法进行
  • 工 厂设计模式
  • 深度学习 vector 之模拟实现 vector (C++)
  • ES6系统学习----从Apollo Client看解构赋值
  • iOS 颜色设置看我就够了
  • js操作时间(持续更新)
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Redis 中的布隆过滤器
  • SegmentFault 2015 Top Rank
  • Selenium实战教程系列(二)---元素定位
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 七牛云假注销小指南
  • 如何设计一个比特币钱包服务
  • 深入浏览器事件循环的本质
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • !$boo在php中什么意思,php前戏
  • #微信小程序(布局、渲染层基础知识)
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (ibm)Java 语言的 XPath API
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (回溯) LeetCode 131. 分割回文串
  • (回溯) LeetCode 40. 组合总和II
  • (算法二)滑动窗口
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET MVC第三章、三种传值方式
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .net8.0与halcon编程环境构建
  • /dev下添加设备节点的方法步骤(通过device_create)
  • [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(十)RCE (远程代码/命令执行漏洞)相关面试题
  • [20150904]exp slow.txt
  • [Android] 修改设备访问权限