【二维动态规划:交错字符串】

介绍

编程语言:Java
本篇介绍一道比较经典的二维动态规划题。
交错字符串

主要说明几点:

  • 为什么双指针解不了?
  • 为什么是二维动态规划?
  • 根据题意分析处转移方程。
  • 严格位置依赖和空间压缩优化。

题目介绍

在这里插入图片描述
题意有点抽象, 看一下示例吧。
在这里插入图片描述
第一种理解方式就是保持两个字符串相对顺序不变, 将s2切割成一个一个子串插入到s1中, 看能否构成字符串s3。

第二种理解方式是字符串s3是根据s1,s2保持相对顺序字符选择的结果。
因为保持各个字符串之间相对顺序不变, 那么可以尝试双指针的思路
比如如上图, s3的第一个和第二个字符只能来自s1, 第3个字符只能来自s2, 第4个字符有讲究了, 它既可能来自s1也可能来自s2, 那么到底来自哪儿, 都有可能!
在这里插入图片描述
下面分析这种双指针为什么是错误的。


双指针的错误解法

部分朋友看到题,相对次序不变, 很容易想到归并排序的merge部分, 自然而然的写出了, 如下代码。

class Solution {public boolean isInterleave(String str1, String str2, String str3) {char[] s1 = str1.toCharArray();char[] s2 = str2.toCharArray();char[] s3 = str3.toCharArray();if(s1.length + s2.length != s3.length){return false;}int p1 = 0, p2 = 0;int i = 0;while(p1<s1.length&&p2<s2.length){if(s1[p1]==s3[i]){p1++;i++;}else if(s2[p2]==s3[i]){p2++;i++;}else{return false;}}while(p1<s1.length){if(s1[p1++]!=s3[i++]){return false;}}while(p2<s2.length){if(s2[p2++]!=s3[i++]){return false;}}return true;}
}

在这里插入图片描述
大部分测试用例能过, 说明双指针的思想大概率是对的, 但是对于某些情况误判了。
误判了什么?
即字符串str1和str2的异步双指针, 在某些情况会出现指向相同字符的情况。
正如下图, 此刻双指针只能固定的选择一种方案, 要么选择str1匹配, 要么选择str2匹配。 实际最终是否能交错处str3, 取决于两种方案的并集。
双指针显然错过了一种方案, 况且这次选择后面可能还要遇见相同字符的分类方案。
在这里插入图片描述
因此, 这道题是动态分析, 应该动态规划分类讨论处理结果。

方法1:递归入手改写记忆化搜索

双指针没有处理分类讨论, 那么通过递归分情况讨论不就好了。
直接定义一个函数f(n), f(s1, p1, s2, p2, s3, p3):s1,s2表示原始的字符数组, s3表示交错出的字符数组。 p1,p2,p3分别表示当前指向各自字符数组的下标。
base case是p3==s3.length, 即字符数组s3被s1,s2各自分配的字符匹配完了。
在保证字符数组不越界的情况, 如果s1,s2当前的字符同时匹配?
通过或逻辑就成功分类讨论了。 取结果的并集。

下面解法会超时, 但已经能过部分样例。
子问题重叠。

class Solution {public boolean isInterleave(String str1, String str2, String str3) {char[] s1 = str1.toCharArray();char[] s2 = str2.toCharArray();char[] s3 = str3.toCharArray();if(s1.length + s2.length != s3.length){return false;}return f(s1,0,s2,0,s3,0);}public static boolean f(char[] s1,int p1, char[]s2, int p2, char[] s3, int p3){if(p3 == s3.length){return true;}//选择方案boolean  ans = false;if(p1 < s1.length && s1[p1] == s3[p3]){ans |= f(s1,p1+1,s2,p2,s3,p3+1);}if(p2 < s2.length && s2[p2] == s3[p3]){ans |= f(s1,p1,s2,p2+1,s3,p3+1);}return ans;}
}

接下用dp表优化
挂个bool dp表, 但是需要注意Java中boolean数组元素默认值是false.
false无法区分这个元素是否被处理过还是这个方案不行。
因此要改进成Boolean数组, 用null区分即可, 处理过的结果要么是false或者true
为什么下面不是改进成三维动态规划?
因为最终情况要么能达到s3.length或者不能, 可以直接确定答案, 因此给dp表再加一维没有必要, 二维dp表就可以完美解决了.

下面代码提交可以打败100%。

class Solution {public boolean isInterleave(String str1, String str2, String str3) {char[] s1 = str1.toCharArray();char[] s2 = str2.toCharArray();char[] s3 = str3.toCharArray();if (s1.length + s2.length != s3.length) {return false;}// 创建dp和visited数组Boolean[][] dp = new Boolean[s1.length + 1][s2.length + 1];return f(s1, 0, s2, 0, s3, 0, dp);}private boolean f(char[] s1, int p1, char[] s2, int p2, char[] s3, int p3, Boolean[][] dp) {if (p3 == s3.length) { // 如果s3遍历完,返回truereturn true;}if (dp[p1][p2] != null) { // 如果已经计算过,直接返回结果return dp[p1][p2];}boolean ans = false;// 尝试从s1取字符匹配if (p1 < s1.length && s1[p1] == s3[p3]) {ans = f(s1, p1 + 1, s2, p2, s3, p3 + 1, dp);}// 如果从s1失败,尝试从s2取字符匹配if (!ans && p2 < s2.length && s2[p2] == s3[p3]) {ans = f(s1, p1, s2, p2 + 1, s3, p3 + 1, dp);}// 记录状态dp[p1][p2] = Boolean.valueOf(ans);return ans;}
}

方法2:直接入手转移方程

dp[i][j], 这里严格定义dp表的含义, 取字符数组s1的前i个, 另取字符数组的前j个, 能否组成s3数组的前i+j个。

s1的[0…i-1]区间, s2的[0…j-1]区间, 能否组成s3[0…i+j-1]的区间。

  1. 如果s1[i-1]==s3[i+j-1], 那么结果依赖dp[i-1][j]。
  2. 如果s2[j-1]==s3[i+j-1], 那么结果依赖dp[i][j-1]。
  3. 如果都不满足, 那么false, 不能交错组成。1,2情况任意一项成立, 那么结果为true。

dp表的简单部分如何填?
思考dp表的定义。
dp[0][0] = = true, 显然。 根据前面的定义, 取字符数组s1的0个, 另取字符数组的0个, 能否组成s3数组的0个。必然可以。

//单独取一个字符数组进行匹配s3前面几个字符for(int i=1;i<=n;i++){if(s1[i-1] != s3[i-1]){break;}dp[i][0] = true;}for(int j=1;j<=m;j++){if(s2[j-1] != s3[j-1]){break;}dp[0][j] = true;}
class Solution {public boolean isInterleave(String str1, String str2, String str3) {if(str1.length() + str2.length() != str3.length() ){return false;}char[] s1 = str1.toCharArray();char[] s2 = str2.toCharArray();char[] s3 = str3.toCharArray();int n = s1.length;int m = s2.length;boolean[][] dp = new boolean[n+1][m+1];dp[0][0] = true;for(int i=1;i<=n;i++){if(s1[i-1] != s3[i-1]){break;}dp[i][0] = true;}for(int j=1;j<=m;j++){if(s2[j-1] != s3[j-1]){break;}dp[0][j] = true;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){dp[i][j] = (s1[i-1]==s3[i+j-1]&&dp[i-1][j])||(s2[j-1]==s3[i+j-1]&&dp[i][j-1]);}}return dp[n][m];}
}

方法3:空间压缩+位置依赖

由于每一行都跟上一行的状态有关, 因此仅需一个一维数组和少数变量就可以滚动更新, 减少内存使用。

从第0行开始自上向下自左向右进行滚动, 压缩成一维dp表。

class Solution {public boolean isInterleave(String str1, String str2, String str3) {if (str1.length() + str2.length() != str3.length()) {return false;}char[] s1 = str1.toCharArray();char[] s2 = str2.toCharArray();char[] s3 = str3.toCharArray();int n = s1.length;int m = s2.length;boolean[] dp = new boolean[m + 1];// 初始化第一列的状态,表示 s1 完全匹配 s3 的情况dp[0] = true;for (int j = 1; j <= m; j++) {if (s2[j - 1] != s3[j - 1]) {break; // 如果 s2[j-1] 和 s3[j-1] 不相等,后续就不需要继续检查了}dp[j] = true; // 初始化 dp[j] 的状态}// 处理 s1 和 s2 的交错匹配for (int i = 1; i <= n; i++) {dp[0] = (s1[i - 1] == s3[i - 1]) && dp[0];  // 更新 dp[0] 状态for (int j = 1; j <= m; j++) {dp[j] = (s1[i - 1] == s3[i + j - 1] && dp[j]) || (s2[j - 1] == s3[i + j - 1] && dp[j - 1]);}}return dp[m];}
}

结语

动态规划的题解好难写…😰😰😰.
空间压缩不会二维dp表真不好分析。

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

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

相关文章

小米路由mini刷PDCN教程补充

花了10天帮助一个网友解决小米路由刷PDCN做打印服务器失败的过程&#xff0c;经历颇多。特别把中间的一些坑写出来&#xff0c;希望大家不要遇到。 首先网上好多教程写的都不错&#xff0c;很适合小白。推荐如下&#xff1a; 刷breed和PDCN方法&#xff1a; 小米路由器mini刷…

Unity ShaderLab 实现交互地毯

实现思路&#xff1a; 将一个位置坐标值传入到shader的顶点着色器中&#xff0c;和这个值位置相同的顶点沿着法线的y轴方向偏移&#xff0c;然后计算这个值与顶点的距离&#xff0c;在这个范围内的顶点&#xff0c;和凸起的点的位置做插值操作。 Shader Graph实现如下&#x…

浏览器语言和Accept-Language请求头指纹

注意&#xff1a;本文仅供学习交流使用 Navigator 对象的语言设置可以帮助网站识别您的首选语言。网站会基于这个值&#xff0c;调整向您呈现内容的所用语言。与其他任意Navigator 对象值一样&#xff0c;它也可用于浏览器指纹识别。 1. 浏览器语言&#xff1a;代表浏览器界面显…

数字IC后端实现之PR工具中如何避免出现一倍filler的缝隙?

在数字IC后端实现中&#xff0c;由于有的工艺foundary不提供Filler1&#xff0c;所以PR工具Innovus和ICC2在做标准单元摆放时需要避免出现两个标准单元之间的缝隙间距是Filler1。为了实现这个目的&#xff0c;我们需要给PR工具施加一些特殊的placement constraint&#xff08;典…

03.ES7 04.ES8

3.1.Array.includes Includes 方法用来检测数组中是否包含某个元素&#xff0c;返回布尔类型值 <script>// includes const mingzhu [王二,张三,李四,王五];//判断console.log(mingzhu.includes(张三));//trueconsole.log(mingzhu.includes(周六));//false//indexOf …

离线安装 Docker-IO:详细步骤指南

离线安装 Docker-IO:详细步骤指南 一、准备工作1.1 下载 Docker 离线安装包1.2 准备安装环境1.3 配置防火墙和 SELinux(可选)二、上传和解压离线安装包2.1 上传安装包2.2 解压安装包三、安装 Docker-IO3.1 移动 Docker 文件到系统目录3.2 配置 Docker 服务3.3 赋予服务文件执…

单细胞细胞通讯全流程分析教程,代做分析和辅导

0. 分析参数文件和细胞通讯的演示数据 0.1 细胞通讯分析总的参数文件&#xff0c;后面部分细胞通讯分析模块会用到 分析参数文件 参数文件名称&#xff1a;total_analysis_params_demo.xlsx &#xff0c;很多分析模块都是这个总的参数文件&#xff0c;我的这个总的参数文件如…

Flume 与 Kafka 整合实战

目录 一、Kafka 作为 Source【数据进入到kafka中&#xff0c;抽取出来】 &#xff08;一&#xff09;环境准备与配置文件创建 &#xff08;二&#xff09;创建主题 &#xff08;三&#xff09;测试步骤 二、Kafka 作为 Sink数据从别的地方抽取到kafka里面】 &#xff08;…

从零开始理解JVM:对象的生命周期之对象销毁(垃圾回收)

一、JVM参数 在学垃圾回收器之前&#xff0c;我们先要知道&#xff0c;jvm参数是怎么回事。因为配置各种回收器&#xff0c;必须对应各种参数设置。 标准参数&#xff08;-&#xff09; 所有的JVM实现都必须实现这些参数的功能&#xff0c;而且向后兼容 -help-version 非标准参…

win10中使用ffmpeg的filter滤镜

1 给视频加文字水印 1.1 添加播放时间 ffmpeg -i input.mp4 -vf "drawtextfontfileC\\:/Windows/fonts/consola.ttf:fontsize30:fontcolorwhite:timecode00\:00\:00\:00:rate25:textTCR\::boxcolor0x000000AA:box1:x20:y20" -y output.mp4 在视频的x20:y20位置添加t…

模拟器快速上手,助力HarmonyOS应用/服务高效开发

文章目录 1 创建模拟器1&#xff09;打开设备管理界面2&#xff09;设置本地模拟器实例存储路径3&#xff09;创建一个模拟器&#xff08;1&#xff09;选择模拟器设备&#xff08;2&#xff09;创建模拟器&#xff08;3&#xff09;启动模拟器&#xff08;4&#xff09;关闭模…

HarmonyOS(61) 组件间状态共享的分类以及状态选择器的选取优先级

状态共享 状态共享的分类状态共享选择器State与prop\Link\ObservedObjectLink组合的区别合理选择装饰器的顺序参考资料 状态共享的分类 HarmonyOS的组件之间是可以共享状态数据了&#xff0c;不同的组件之间&#xff0c;状态共享的场景也不一样&#xff0c;根据共享范围从小到…

高德地图 Readme GT 定制版 10.25.0.3249 | 极致简洁

这款定制版高德地图去除了广告&#xff0c;运行速度更快。虽然没有车道级导航、打车功能和红绿灯倒计时等功能&#xff0c;但支持正常登录和收藏功能。检测更新始终为最新版本。 大小&#xff1a;82.5M 下载地址&#xff1a; 百度网盘&#xff1a;https://pan.baidu.com/s/1Y…

去中心化物理基础设施网络(DePIN):重塑未来的基石

一、引言&#xff1a;DePIN的定义与背景 什么是DePIN&#xff1f; 去中心化物理基础设施网络&#xff08;DePIN&#xff0c;Decentralized Physical Infrastructure Networks&#xff09;是利用区块链和去中心化技术管理、优化和激励物理资源分配的一种新兴模式。与传统集中式…

模型 布鲁姆法则

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。分层提升思维力。 1 布鲁姆法则的应用 1.1 布鲁姆法则在产品开发流程中的应用 背景&#xff1a; 在产品开发领域&#xff0c;创新和效率是关键。布鲁姆法则可以帮助产品经理和设计师系统地提升产品开…

恒创科技:服务器操作系统和客户端操作系统之间的区别

客户端操作系统和服务器操作系统是两种不同的操作系统&#xff0c;旨在满足计算机网络环境中的特定目的。虽然每种类型的操作系统在基本功能方面都有一些相似之处&#xff0c;但它们针对不同的用例进行了优化&#xff0c;并具有针对其特定角色量身定制的特定功能。 什么是服务器…

Flink的双流join理解

如何保证Flink双流Join准确性和及时性、除了窗口join还存在哪些实现方式、究竟如何回答才能完全打动面试官呢。。你将在文中找到答案。 1 引子 1.1 数据库SQL中的JOIN 我们先来看看数据库SQL中的JOIN操作。如下所示的订单查询SQL&#xff0c;通过将订单表的id和订单详情表ord…

2024学习之前端微信小程序开发教程,从入门到精通-含基础+实战+源码code

目录 一、简单介绍 二、课程需知 三、内容编排 1、小程序基础  起步式 目录结构 小程序框架 场景值  逻辑层 视图层 组件 视图容器 基础内容 表单组件 导航 媒体组件 Api 路由 界面 交互 网络 数据缓存 自定义组件 2、项目实战 …

HarmonyOS4+NEXT星河版入门与项目实战(23)------组件转场动画

文章目录 1、控件图解2、案例实现1、代码实现2、代码解释3、实现效果4、总结1、控件图解 这里我们用一张完整的图来汇整 组件转场动画的用法格式、属性和事件,如下所示: 2、案例实现 这里我们对上一节小鱼游戏进行改造,让小鱼在游戏开始的时候增加一个转场动画,让小鱼自…

数据预处理方法—特征选择、特征缩放、特征构造

特征选择 1.1 原理 特征选择是选择对模型训练最重要的特征&#xff0c;减少数据维度&#xff0c;去除冗余或不相关特征&#xff0c;提高模型性能的性能和训练速度&#xff0c;减少过拟合。 1.2 核心公式 可以使用基于树模型的特征重要性度量&#xff0c;如在随机森林中计算特…