【区间 DP】运用区间 DP 解决古老原题

题目描述

这是 LeetCode 上的 「664. 奇怪的打印机」 ,难度为 「困难」

Tag : 「区间 DP」

有台奇怪的打印机有以下两个特殊要求:

  • 打印机每次只能打印由 同一个字符 组成的序列。
  • 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。

给你一个字符串 s ,你的任务是计算这个打印机打印它需要的最少打印次数。

示例 1:

输入:s = "aaabbb"

输出:2

解释:首先打印 "aaa" 然后打印 "bbb"

示例 2:

输入:s = "aba"

输出:2

解释:首先打印 "aaa" 然后在第二个位置打印 "b" 覆盖掉原来的字符 'a'

提示:

  • s 由小写英文字母组成

基本分析

首先,根据题意我们可以分析出一个重要推论:连续相同的一段字符,必然可以归到同一次打印中,而不会让打印次数变多。注意,这里说的是「归到一次」,而不是说「单独作为一次」

怎么理解这句话呢?

举个 🌰,对于诸如 ...bbaaabb... 的样例数据,其中多个连续的 a 必然可以归到同一次打印中,但这一次打印可能只是将 aaa 作为整体进行打印;也有可能是 aaa 与前面或者后面的 a 作为整体被打印(然后中间的 b 被后来的打印所覆盖)。但无论是何种情况连续一段的 aaa 必然是可以「归到同一次打印」中。

我们可以不失一般性证明「连续相同的一段字符,必然可以归到同一次打印中,而不会让打印次数变多」这个推理是否正确:

假设有目标序列 其中 连续一段字符相同,假如这一段的打印被最后完成(注意最后完成不代表这一段要保留空白,这一段可以此前被打印多次),除了这一段以外所消耗的打印次数为 ,那么根据 不同的打印方案有:

  1. 单纯划分为多段:总共打印的次数大于 (此方案不会取到打印最小值 ,可忽略)
  2. 归到同一次打印:总共打印的次数等于
  3. 结合之前的打印划分为多段,即 一段的两段本身就是「目标字符」,我们本次只需要打印 中间的部分。总共打印的次数等于

由于同样的地方可以被重复打印,因此我们可以将情况 中打印边缘扩展到 处,这样最终打印结果不变,而且总的打印次数没有增加。

到这一步,我们其实已经证明出「连续相同的一段字符,必然可以归到同一次打印中,而不会让打印次数变多」的推论成立了。

但可能会有同学提出疑问:怎么保证 是被最后涂的?怎么保证 不是和其他「不相邻的同样字符」一起打印的?

答案是不用保证,因为不同状态(打印结果)之间相互独立,而有明确的最小转移成本。即从当前打印结果 a 变成打印结果 b,是具有明确的最小打印次数的(否则本题无解)。因此我们上述的分析可以看做任意两个中间状态转移的“最后一步”,而且不会整体的结果。

对应到本题,题目给定的起始状态是空白字符串 a,目标状态是入参字符串 s。那么真实最优解中,从 a 状态到 s 状态中间可能会经过任意个中间状态,假设有两个中间状态 pq,那么我们上述的分析就可以应用到中间状态 pq 的转移中,可以令得 pq 转移所花费的转移成本最低(最优),同时这个转移不会影响「ap 的转移」和「qs 的转移」,是相互独立的。

因此这个分析可以推广到真实最优转移路径中的任意一步,是一个具有一般性的结论。

上述分析是第一个切入点,第二个切入点是「重复打印会进行覆盖」,这意味着我们其实不需要确保 这一段在目标字符串中完全相同,而只需要 相同即可,即后续打印不会从边缘上覆盖 区间的原有打印,否则 这一段的打印就能用范围更小的区间所代替。

这样就引导出我们状态转移的关键:状态转移之间只需要确保首位字符相同。

动态规划

定义 为将 这一段打印成目标结果所消耗的最小打印次数。

不失一般性考虑 该如何转移:

  • 只染 这个位置,此时
  • 不只染 这个位置,而是从 染到 (需要确保首位相同 ):

其中状态转移方程中的情况 需要说明一下:由于我们只确保 ,并不确保 之间的字符相同,根据我们基本分析可知, 这个点可由打印 的时候一同打印,因此本身 并不独立消耗打印次数,所以这时候 这一段的最小打印次数应该取 ,而不是

最终的 为上述所有方案中取

代码:

class Solution {
    public int strangePrinter(String s) {
        int n = s.length();
        int[][] f = new int[n + 1][n + 1];
        for (int len = 1; len <= n; len++) {
            for (int l = 0; l + len - 1 < n; l++) {
                int r = l + len - 1;
                f[l][r] = f[l + 1][r] + 1;
                for (int k = l + 1; k <= r; k++) {
                    if (s.charAt(l) == s.charAt(k)) {
                        f[l][r] = Math.min(f[l][r], f[l][k - 1] + f[k + 1][r]);
                    }
                }
            }
        }
        return f[0][n - 1];
    }
}
  • 时间复杂度:
  • 空间复杂度:

总结

这道题的原型应该出自 String painter : http://acm.hdu.edu.cn/showproblem.php?pid=2476。

如果只是为了把题做出来,难度不算特别大,根据数据范围 ,可以猜到是 做法,通常就是区间 DP 的「枚举长度 + 枚举左端点 + 枚举分割点」的三重循环。

但是要搞懂为啥可以这样做,还是挺难,大家感兴趣的话可以好好想想 ~ 🤣

最后

这是我们「刷穿 LeetCode」系列文章的第 No.664 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

本文由 mdnice 多平台发布

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

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

相关文章

DNDC模型土壤碳储量、温室气体排放、农田减排、土地变化、气候变化中的实践应用

查看原文>>>DNDC模型土壤碳储量、温室气体排放、农田减排、土地变化、气候变化中的实践应用 目录 一、DNDC模型介绍 二、DNDC初步操作 三、遥感和GIS基础 四、DNDC气象数据 五、DNDC土地数据 六、DNDC土壤数据 七、DNDC结果分析 八、DNDC率定验证 九、土壤碳…

autox.js的三个版本universal、armeabi-v7a、arm64-v8a的区别

APK版本说明&#xff1a; universal: 通用版&#xff08;不在乎安装包大小/懒得选就用这个版本&#xff0c;包含以下2种CPU架构so&#xff09; armeabi-v7a: 32位ARM设备&#xff08;备用机首选&#xff09; arm64-v8a: 64位ARM设备&#xff08;主流旗舰机&#xff09; ABI在…

【Hello Algorithm】暴力递归到动态规划(四)

动态规划的数组压缩技巧 - 机器人走格子问题 题目是leetcode62题目原题 表示如下 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中…

分享Java NET Python三大技术下AutojsPro7云控代码

引言 有图有真相&#xff0c;那短视频就更是真相了。下面是三大语言的短视频。 Java源码版云控示例&#xff1a; Java源码版云控示例在线视频 Net源码版云控示例&#xff1a; Net源码版云控示例在线视频亚丁号-知识付费平台 支付后可见 扫码付费可见 Python源码版云控示例&…

openGauss Meetup(天津站)精彩回顾 | openGauss天津用户组正式成立

由openGauss社区、天开发展集团、天津市软件行业协会、天大智图&#xff08;天津&#xff09;科技有限公司联合主办的“openGauss Meetup • 天津站”已于10月13日落下帷幕&#xff0c;此次活动邀请到众多业内技术专家&#xff0c;从技术创新、学术创新、发展创新、以及生态共建…

【Python机器学习】零基础掌握CalibratedClassifierCV概率校准

有没有想过如何提高分类模型的可靠性? 在现实生活中,许多决策都依赖于分类模型。例如在医疗诊断中,一个模型可能用于预测一个肿瘤是良性还是恶性的。但是这些模型有时会给出不准确的概率估计。 考虑一个场景,医生使用一个模型来预测肿瘤性质。假设有以下模拟数据: 患者I…

基于内存的分布式NoSQL数据库Redis(五)数据存储与RDB设计

文章目录 知识点18&#xff1a;数据存储设计知识点19&#xff1a;Redis持久化&#xff1a;RDB设计知识点20&#xff1a;Redis持久化&#xff1a;RDB测试后记 知识点18&#xff1a;数据存储设计 目标&#xff1a;掌握常见数据存储的设计 实施 问题 数据存储如何保证数据安全&am…

QT实现凸凹边形等距缩放

参考&#xff1a;https://blog.csdn.net/weixin_39383896/article/details/99615371和https://blog.csdn.net/qq_15821883/article/details/117421400 代码逻辑思路&#xff1a; 1、获取向量AB、BC的坐标。 2、计算向量AB、BC的长度。 3、根据点乘获取cosθ大小。 4、根据cosθ…

详解如何利用PHP实现RPC

一、什么是RPC 什么是RPC RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种计算机通信协议&#xff0c;用于使一个计算机程序可以调用另一个运行在不同计算机上的程序的过程或函数&#xff0c;并且无需了解底层网络细节。简而言之&#xff0c…

测试PySpark

文章最前&#xff1a; 我是Octopus&#xff0c;这个名字来源于我的中文名--章鱼&#xff1b;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github &#xff1b;这博客是记录我学习的点点滴滴&#xff0c;如果您对 Python、Java、AI、算法有兴趣&#xff0c;可以关注我的…

数据结构之手撕顺序表(讲解➕源代码)

0.引言 在本章之后&#xff0c;就要求大家对于指针、结构体、动态开辟等相关的知识要熟练的掌握&#xff0c;如果有小伙伴对上面相关的知识还不是很清晰&#xff0c;要先弄明白再过来接着学习哦&#xff01; 那进入正题&#xff0c;在讲解顺序表之前&#xff0c;我们先来介绍…

代码随想录算法训练营第23期day25| 216.组合总和III 、17.电话号码的字母组合

目录 一、&#xff08;leetcode 216&#xff09;组合总和III 剪枝 二、&#xff08;leetcode 17&#xff09;电话号码的字母组合 思路 一、&#xff08;leetcode 216&#xff09;组合总和III 力扣题目链接 状态&#xff1a;已AC&#xff0c;就是在77题的前提下&#xff0c…

Unity3D 程序员常用的核心类及方法详解

Unity3D是一款强大的游戏引擎&#xff0c;广泛应用于游戏开发领域。作为Unity3D程序员&#xff0c;掌握常用的核心类及方法是非常重要的。本文将详细介绍Unity3D中程序员常用的核心类及方法&#xff0c;并给出代码实现。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff…

基于ssm的旅游管理系统

功能如下图所示 摘要 基于SSM框架的旅游管理系统代表了信息技术在旅行业中的崭新机遇&#xff0c;为旅行企业提供了强大的工具&#xff0c;以应对现代旅游市场的复杂挑战。这个系统的研发和实施具有广泛的研究意义&#xff0c;它深刻影响了旅游业的发展&#xff0c;具体表现如下…

简单测试一下 展锐的 UDX710 性能

最近在接触 联通5G CPE VN007 &#xff0c;发现使用的是 展锐的Unisoc UDX710 CPU&#xff0c;正好简单的测试一下这颗CPU CPU信息 UDX710 是一颗 双核 ARM Cortex-A55 处理器&#xff0c;主频高达 1.35GHz processor : 0 BogoMIPS : 52.00 Features : fp…

QT最小化到托盘显示

一、效果&#xff1a; 程序关闭后&#xff0c;程序并没有退出&#xff0c;而是放入了托盘中&#xff1b;点击恢复原始大小&#xff0c;或者双击托盘图标&#xff0c;可以恢复程序原来的窗口。如下图。 那qt是如何实现这样的办法呢&#xff0c;其实就是用到了 QSystemTrayIcon类…

2023.10.17 关于 wait 和 notify 的使用

目录 引言 方法的使用 引入实例&#xff08;wait 不带参数版本&#xff09; wait 方法执行流程 wait 和 notify 组合实例 wait 带参数版本 notify 和 notifyAll 的区别 经典例题 总结 引言 线程最大的问题是抢占式执行&#xff0c;随机调度虽然线程在内核里的调度是随…

SpringBoot_redis使用实战(四)_消息模式

redis消息 1.简介2.入门2.1 编写消息监听器2.2 注册消息监听器2.3 发送消息 3.进阶3.1ChannelTopic和PatternTopic3.1.1. ChannelTopic3.1.2. PatternTopic 3.2 可靠性 4.总结 1.简介 MessageListener是Spring Data Redis中的一个接口&#xff0c;它定义了处理接收到的Redis消…

c++ 高效使用vector(面试)

文章目录 1.善用Reserve提前分配足够的空间2. 使用 shrink_to_fit() 释放 vector 占用的内存&#xff0c; – clear() 或 erase() 不会释放内存3 在填充或者拷贝到 vector 的时候&#xff0c;应该使用赋值而不是 insert() 或push_back()4 遍历 std::vector 元素的时候&#xff…

Maven多模块管理(转载)

注意&#xff1a;父模块需设定打包方式为pom https://cloud.tencent.com/developer/article/1667275 dependencyManagement 统一管理子类依赖版本 在父类maven中加入&#xff0c;不会继承给子类&#xff0c;只能规定子类的依赖版本&#xff0c;子类加入dependence后无需写入 …