LeetCode 1653. Minimum Deletions to Make String Balanced【字符串,动态规划,枚举】中等

news2024/10/2 10:39:19

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

You are given a string s consisting only of characters 'a' and 'b'​​​​.

You can delete any number of characters in s to make s balanced. s is balanced if there is no pair of indices (i,j) such that i < j and s[i] = 'b' and s[j]= 'a'.

Return the minimum number of deletions needed to make s balanced.

Example 1:

Input: s = "aababbab"
Output: 2
Explanation: You can either:
Delete the characters at 0-indexed positions 2 and 6 ("aababbab" -> "aaabbb"), or
Delete the characters at 0-indexed positions 3 and 6 ("aababbab" -> "aabbbb").

Example 2:

Input: s = "bbaaaaabb"
Output: 2
Explanation: The only solution is to delete the first two characters.

Constraints:

  • 1 <= s.length <= 105
  • s[i] is 'a' or 'b'​​.

题意:给出一个仅由 'a', 'b' 组成的字符串 s ,如果任意下标对 (i, j), i < j ,都不存在 s[i] == 'b' and s[j] == 'a' ,就称 s平衡的。你可以删除 s 中任意数量的字符,使 s 平衡。返回最少的使 s 平衡的删除数。


解法1 枚举分割线(两次遍历)

完全不需要过多的思考。由于平衡字符串的 'a' 都在 'b' 的左边,因此一定存在一条分割线,将 s 分成前后两部分,'a' 都在前面一部分,'b' 都在后面一部分。反过来说,要删除的 'b' 都在前一部分,要删除的 'a' 都在后一部分。我们要做的就是,枚举所有 n + 1 n +1 n+1 条分割线,计算删除次数的最小值,就是答案。

最简单的实现如下所示。第一次遍历计算 'a' 的总个数 a a a ,并将其作为「以下标 0 0 0 之前作为分割线、要删除全部 'a' 」的删除次数 m i n D e l minDel minDel ;第二次遍历时,当前字符如果是 'a' 就令 ta 加1,并以下标 i i i 之后作为分割线,然后计算出前一部分中 'b' 的个数 i + 1 − t a i + 1 - ta i+1ta 、和后一部分中 'a' 的个数 a − t a a - ta ata ,这二者之和与 m i n D e l minDel minDel 比较求最小值:

class Solution {
    public int minimumDeletions(String s) {
        int a = 0, ta = 0, n = s.length();
        for (int i = 0; i < n; ++i)
            if (s.charAt(i) == 'a') ++a;
        int minDel = a; // 以下标0之前作为a,b分割线,要删除全部a
        for (int i = 0; i < n; ++i) { // 以下标i之后作为分割线
            if (s.charAt(i) == 'a') ++ta;
            int bComeBeforeA = i + 1 - ta;
            // ta bComeBeforeA | a-ta n-a-bComeBeforeA
            // 删除前面的bComeBeforeA个b和后面的a-ta个a
            minDel = Math.min(minDel, bComeBeforeA + a - ta);
        }
        return minDel;
    }
}

上面的写法简单易懂,但我们还可稍微优化一下。

  • 去掉if语句,如第一次遍历中改为 a += 'b' - s.charAt(i) ;第二次遍历中改为 (s.charAt(i) - 'a') * 2 - 1 。从而避免因条件跳转指令而出现的CPU分支预测。
  • 第一次遍历仍然是统计 s s s'a' 的个数,并将其作为 d e l del del 。只是第二次遍历时,下标 i i i 变大的过程就是分割线不断右移的过程,我们每遇到一个 'a' ,就相当于将一个 'a' 纳入前一部分,从而删除 'a' 的次数减1;每遇到一个 'b' ,就相当于要删除的 'b' 的次数加1。这样不断减少或增加删除次数,并用 a n s ans ans 统计所有 d e l del del 中的最小值。示例图(借用灵茶山艾府大佬画的图)如下:
    300
class Solution {
    public int minimumDeletions(String s) {
        int del = 0, n = s.length();
        for (int i = 0; i < n; ++i)
            del += ('b' - s.charAt(i));
        int ans = del;
        for (int i = 0; i < n; ++i) { // 以下标i之后作为分割线
            del += (s.charAt(i) - 'a') * 2 - 1; // 'a':-1,'b':1
            ans = Math.min(del, ans);
        }
        return ans;
    }
}

解法2 动态规划(1次遍历)

f [ i ] f[i] f[i] 为使 s[0...i] 平衡的最少删除次数,考虑 s[0...i] 的最后一个字母:

  • 如果它是 'b' ,则无需删除,问题规模缩小,变为「使 s[0...i-1] 平衡的最少删除次数」即 f [ i ] = f [ i − 1 ] f[i] = f[i-1] f[i]=f[i1]
  • 如果它是 'a'
    • 删除它,则答案为「使 s[0...i-1] 平衡的最少删除次数」加上1,即 f [ i − 1 ] + 1 f[i - 1] + 1 f[i1]+1
    • 保留它,则前面所有的 'b' 都要删除。
    • bComeBeforeAs[0...i]'b' 的个数,从而 f[i] = min(f[i - 1] + 1, bComeBeforeA)

代码实现时,可以只用一个变量表示 f f f

class Solution {
    public int minimumDeletions(String s) {
        int bComeBeforeA = 0, minDel = 0;
        for (int i = 0, n = s.length(); i < n; ++i) {
            char c = s.charAt(i);
            // c前面的字符串已经平衡,如果前面存在'b',则最后的'a'会失衡
            if (c == 'a') {
                // 要么删除前面的'b',要么删除这里的'a'
                // 看哪种做法删除数最少
                minDel = Math.min(bComeBeforeA, minDel + 1);
            } else ++bComeBeforeA; // 最后一个字符是'b',不会导致失衡
        }
        return minDel;
    }
}

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

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

相关文章

shell基本知识

为什么学习和使用Shell编程 什么是Shell shell的起源 shell的功能 shell的分类 如何查看当前系统支持的shell&#xff1f; 如何查看当前系统默认shell&#xff1f; 驼峰语句 shell脚本的基本元素 shell脚本编写规范 shell脚本的执行方式 shell脚本的退出状态 &#xf…

数位dp-- 数字游戏

题目 思路 也是一道比较典型的数位dp的问题&#xff0c;关键的思想跟我上一篇博客很像&#xff0c; 首先把区间值变成[1,Y]-[1,X-1]的值&#xff0c;然后单独计算得到结果。 总的来说就是把这个数的每一位都单独拿出来&#xff0c;然后根据选0-an-1和选**an**两种方案单独计算&…

LeetCode 热题 C++ 538. 把二叉搜索树转换为累加树 543. 二叉树的直径 560. 和为 K 的子数组

538. 把二叉搜索树转换为累加树 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xff0c;二…

Java基础算法题

目录 练习一 : 优化代码 扩展 : CRTL Alt M 自动抽取方法 练习二: 方法1: 方法2: 方法3: Math : 顾名思义&#xff0c;Math类就是用来进行数学计算的&#xff0c;它提供了大量的静态方法来便于我们实现数学计算&#xff1a; 练习三 : 练习四 : 练习五: 练习…

【GO】K8s 管理系统项目34[Docker方式–应用部署]

K8s 管理系统项目[Docker方式–应用部署] 1. 数据库 1.1 创建数据库目录 mkdir -p /data/mysql5.7/1.2 创建容器 docker run --name mysql -itd -h mysql-server -e MYSQL_ROOT_PASSWORDroot -v /data/mysql5.7:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 …

layui框架实战案例(19):layui-table模块表格综合应用(筛选查询、导入导出、群发短信、一键审核、照片展示、隐私加密)

系列文章目录 layui动态表格翻页和搜索的代码分析layui框架实战案例(3)&#xff1a;layui上传错误请求上传接口出现异常解决方案layui框架实战案例(9)&#xff1a;layPage 静态数据分页组件layui框架实战案例(10)&#xff1a;短信验证码60秒倒计时layui框架实战案例(11)&#…

《实践论》笔记及当下反思(二)

目录 笔记 1、马克思主义所说的绝对真理是什么&#xff1f; 2、客观现实世界的变化运动永远没有完结&#xff0c;人们在实践中对于真理的认识也就永远没有完结 3、改造客观世界&#xff0c;也改造自己的主观世界-——改造自己的认识能力 4、实践、认识、再实践、再认识&…

ARP报文内容详细分析

ARP报文格式如图&#xff1a; 字段1&#xff1a;ARP请求的目的以太网地址&#xff0c;全1时&#xff0c;代表广播地址。 字段2&#xff1a;发送ARP请求的以太网地址。 字段3&#xff1a;以太网帧类型表示后面的数据类型&#xff0c;ARP请求和ARP应答此字段为&#xff1a;0x0806…

SourceTree 重置提交、合并、撤销、回滚

SourceTree重置当前分支到此次提交使用场景&#xff1a;已提交未推送的修改撤销、想把某一次的错误修改全部撤销当前发布代码有bug需要切到上次提交发布版本Git中的HEAD解释# 使用最新一次提交重制暂存区git reset HEAD -- filename# 使用最新一次提交重制暂存区和工作区git re…

SpringBoot——统一功能处理

处理登陆拦截 上一片博客中讲到SpringAOP可以对页面进行拦截&#xff0c;我们可以用SpringAOP实现对登陆的拦截 但是由于拦截需要HttpSession对象&#xff0c;并且之后还需要页面重定向&#xff0c;因此在实际应用中&#xff0c;并不用SpringAOP进行登陆拦截&#xff0c;而是…

HBase---idea操作Hbase数据库并且映射到Hive

idea操作Hbase数据库并且映射到Hive 文章目录idea操作Hbase数据库并且映射到Hiveidea操作Hbase数据库环境准备启动服务创建Maven工程在测试类中编写初始化方法在测试类中编写关闭方法在测试类中编写创建命名空间方法在测试类中编写创建表方法在测试类中编写查看表结构方法在测试…

tmall.product.match.schema.get( 获取匹配产品规则 )

&#xffe5;免费必须用户授权 ISV发布商品前&#xff0c;需要先查找到产品ID&#xff0c;这个接口返回查找产品规则入参规则 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 点击获取key和secret 请求示例 TaobaoClient …

ORACLE SQL格式化小数点

ORACLE SQL格式化小数点 select CONCAT(TO_CHAR(0.00100,‘990.999’),‘%’) as a0 , CONCAT(TO_CHAR(1100,‘990.999’),‘%’) as a1 , CONCAT(TO_CHAR(0.236100,‘990.999’),‘%’) as a2 , CONCAT(TO_CHAR(0.0200100,‘990.999’),‘%’) as a3 , CONCAT(TO_CHAR(1.0310…

状态机的Go语言实现版本

一、状态机 1. 定义 有限状态机&#xff08;Finite-state machine, FSM&#xff09;&#xff0c;简称状态机&#xff0c;是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。 2. 组成要素 现态&#xff08;src state&#xff09;&#xff1a;事务当前所处的状…

Java程序中空指针异常的最佳实践

1、空指针问题 NullPointerException 是 Java 代码中最常见的异常&#xff0c;将其最可能出现的场景归为以下 5 种&#xff1a; 参数值是 Integer 等包装类型&#xff0c;使用时因为自动拆箱出现了空指针异常&#xff1b;字符串比较出现空指针异常&#xff1b;诸如 Concurren…

新建vite+vue3+ts项目,以及解决过程中遇到的问题

目录 一、新建vitevue3ts项目 二、解决过程中遇到的问题 解决报错&#xff1a;Module ‘“xx.vue“‘ has no default export. 解决报错&#xff1a;Error [ERR_MODULE_NOT_FOUND]: Cannot find package ‘uuid’ imported from xxx的解决 解决报错&#xff1a;[plugin:vi…

如何选择一款数据库?

1主流数据库技术介绍常见的数据库模型主要分为SQL关系型数据库和NoSQL非关系型数据库。其中关系型数据库分为传统关系数据库和大数据数据库&#xff0c;非关系型数据库分为键值存储数据库、列存储数据库、面向文档数据库、图形数据库、时序数据库、搜索引擎存储数据库及其他&am…

Java集合类

在了解Java集合类之前&#xff0c;我们必须首先了解java.util包&#xff0c;这个包提供了所有集合相关的接口和类。 集合是用来存储数据类型的一种数据结构&#xff0c;提到存储数据类型的数据结构我们最先想到的应该是数组&#xff0c;接下来我们简单回顾下Java中集合和数组的…

Xilinx ZYNQ 7000 HDMI

High-Definition Multimedia Interface (HDMI) 参考xilinx application note XAPP460 HDMI来自High-Definition Multimedia Interface 高分辨率多媒体接口&#xff0c;多媒体一般包含图像和声音。 Transition Minimized Differential Signaling (TMDS) 是HDMI的物理层。 TMDS…

春招冲刺(十一):前端面试之网络总结

网络总结 Q1: GET和POST的请求的区别 应用场景&#xff1a;Get是一个幂等请求&#xff0c;一般用于请求资源。post不是幂等请求&#xff0c;一般用于修改资源。缓存&#xff1a;Get请求一般缓存&#xff0c;Post一般不缓存报文格式&#xff1a;Get请求体一般为空&#xff0c;…