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

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

介绍

编程语言: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表真不好分析。

相关文章:

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

介绍 编程语言&#xff1a;Java 本篇介绍一道比较经典的二维动态规划题。 交错字符串 主要说明几点&#xff1a; 为什么双指针解不了&#xff1f;为什么是二维动态规划&#xff1f;根据题意分析处转移方程。严格位置依赖和空间压缩优化。 题目介绍 题意有点抽象&#xff0c…...

goframe开发一个企业网站 MongoDB 完整工具包18

1. MongoDB 工具包完整实现 (mongodb.go) package mongodbimport ("context""fmt""time""github.com/gogf/gf/v2/frame/g""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options" )va…...

在vue中,根据后端接口返回的文件流实现word文件弹窗预览

需求 弹窗预览word文件&#xff0c;因浏览器无法直接根据blob路径直接预览word文件&#xff0c;所以需要利用插件实现。 解决方案 利用docx-preview实现word文件弹窗预览&#xff0c;以node版本16.21.3和docx-preview版本0.1.8为例 具体实现步骤 1、安装docx-preview插件 …...

动态规划之背包问题

0/1背包问题 1.二维数组解法 题目描述&#xff1a;有一个容量为m的背包&#xff0c;还有n个物品&#xff0c;他们的重量分别为w1、w2、w3.....wn&#xff0c;他们的价值分别为v1、v2、v3......vn。每个物品只能使用一次&#xff0c;求可以放进背包物品的最大价值。 输入样例…...

【Python】 深入理解Python的单元测试:用unittest和pytest进行测试驱动开发

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 单元测试是现代软件开发中的重要组成部分,通过验证代码的功能性、准确性和稳定性,提升代码质量和开发效率。本文章深入介绍Python中两种主流单元测试框架:unittest和pytest,并结合测试驱动开发(TDD)…...

Java集合1.0

1.什么是集合&#xff1f; 集合就是一个存放数据的容器&#xff0c;准确的说是放数据对象引用的容器。 集合和数组的区别 数组是固定长度&#xff0c;集合是可变长度。数组可以存储基本数据类型&#xff0c;也可以存储引用数据类型&#xff0c;集合只能存储引用数据类型&…...

Leetcode 336 回文对

示例 1&#xff1a; 输入&#xff1a;words ["abcd","dcba","lls","s","sssll"] 输出&#xff1a;[[0,1],[1,0],[3,2],[2,4]] 解释&#xff1a;可拼接成的回文串为 ["dcbaabcd","abcddcba","sl…...

实现一个可配置的TCP设备模拟器,支持交互和解析配置

前言 诸位在做IOT开发的时候是否有遇到一个问题&#xff0c;那就是模拟一个设备来联调测试&#xff0c;虽然说现在的物联网通信主要是用mqtt通信&#xff0c;但还是有很多设备使用TCP这种协议交互&#xff0c;例如充电桩&#xff0c;还有一些工业设备&#xff0c;TCP这类报文交…...

算法的空间复杂度

空间复杂度 空间复杂度主要是衡量一个算法运行所需要的额外空间&#xff0c;在计算机发展早期&#xff0c;计算机的储存容量很小&#xff0c;所以空间复杂度是很重要的。但是经过计算机行业的迅速发展&#xff0c;计算机的容量已经不再是问题了&#xff0c;所以如今已经不再需…...

自定义协议

1. 问题引入 问题&#xff1a;TCP是面向字节流的&#xff08;TCP不关心发送的数据是消息、文件还是其他任何类型的数据。它简单地将所有数据视为一个字节序列&#xff0c;即字节流。这意味着TCP不会对发送的数据进行任何特定的边界划分&#xff0c;它只是确保数据的顺序和完整…...

在 Taro 中实现系统主题适配:亮/暗模式

目录 背景实现方案方案一&#xff1a;CSS 变量 prefers-color-scheme 媒体查询什么是 prefers-color-scheme&#xff1f;代码示例 方案二&#xff1a;通过 JavaScript 监听系统主题切换 背景 用Taro开发的微信小程序&#xff0c;需求是页面的UI主题想要跟随手机系统的主题适配…...

autogen框架中使用chatglm4模型实现react

本文将介绍如何使用使用chatglm4实现react&#xff0c;利用环境变量、Tavily API和ReAct代理模式来回答用户提出的问题。 环境变量 首先&#xff0c;我们需要加载环境变量。这可以通过使用dotenv库来实现。 from dotenv import load_dotenv_ load_dotenv()注意.env文件处于…...

读《Effective Java》笔记 - 条目9

条目9&#xff1a;与try-finally 相比&#xff0c;首选 try -with -resource 什么是 try-finally&#xff1f; try-finally 是 Java 中传统的资源管理方式&#xff0c;通常用于确保资源&#xff08;如文件流、数据库连接等&#xff09;被正确关闭。 BufferedReader reader n…...

【软件入门】Git快速入门

Git快速入门 文章目录 Git快速入门0.前言1.安装和配置2.新建版本库2.1.本地创建2.2.云端下载 3.版本管理3.1.添加和提交文件3.2.回退版本3.2.1.soft模式3.2.2.mixed模式3.2.3.hard模式3.2.4.使用场景 3.3.查看版本差异3.4.忽略文件 4.云端配置4.1.Github4.1.1.SSH配置4.1.2.关联…...

nextjs window is not defined

问题产生的原因 在 Next.js 中&#xff0c;“window is not defined” 错误通常出现在服务器端渲染&#xff08;Server - Side Rendering&#xff0c;SSR&#xff09;的代码中。这是因为window对象是浏览器环境中的全局对象&#xff0c;在服务器端没有window这个概念。例如&am…...

C语言实现冒泡排序:从基础到优化全解析

一、什么是冒泡排序&#xff1f; 冒泡排序&#xff08;Bubble Sort&#xff09;是一种经典的排序算法&#xff0c;其工作原理非常直观&#xff1a;通过多次比较和交换相邻元素&#xff0c;将较大的元素“冒泡”到数组的末尾。经过多轮迭代&#xff0c;整个数组会变得有序。 二…...

windows11下git与 openssl要注意的问题

看了一下自己贴文的历史&#xff0c;有一条重要的忘了写了。 当时帮有位同事配置gitlab&#xff0c;众说周知gitlab是不太好操作。 但我还是自认自己git还是相当熟的。 解决了一系列问题&#xff0c;如配置代理&#xff0c;sshkey&#xff0c;私有库&#xff0c;等等&#xff0…...

lua除法bug

故事背景&#xff0c;新来了一个数值&#xff0c;要改公式。神奇的一幕出现了&#xff0c;公式算出一个非常大的数。排查是lua有一个除法bug,1除以大数得到一个非常大的数。 function div(a, b)return tonumber(string.format("%.2f", a/b)) end print(1/73003) pri…...

Ubuntu下Docker容器java服务往mysql插入中文数据乱码

一、问题描述 1、java服务部署在ubuntu下的docker容器内&#xff0c;但是会出现部分插入中文数据显示乱码&#xff0c;如图所示&#xff1a; 二、解决方案 1、查看mysql是否支持utf8&#xff0c;登录进入Mysql 输入命令&#xff1a; mysql -u root -pshow variables like c…...

C语言根据字符串变量获取/设置结构体成员值

一、背景 在项目中需要根据从数据库中获取的字段与对应的键值付给对应结构体成员上&#xff0c;而c语言中没有类似的反射机制&#xff0c;所以需要实现类似功能。例&#xff0c;从表中查到a 10&#xff0c;在结构体t中&#xff0c;需要将 t.a 10。 二、实现 感谢ChatGPT&…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...