LeetCode429周赛T4

embedded/2024/12/27 16:03:27/

最小化二进制字符串中最长相同子字符串的长度

在处理二进制字符串问题时,优化字符串结构以满足特定条件是一项常见的挑战。本文将探讨一个具体的问题:给定一个长度为 n 的二进制字符串 s 和一个整数 numOps,通过最多 numOps 次位翻转操作,最小化字符串 s 中最长相同子字符串的长度。

问题描述

输入

  • 字符串 s:长度为 n 的二进制字符串,仅由字符 '0''1' 组成。
  • 整数 numOps:允许执行的最大位翻转次数。

操作

在每次操作中,你可以选择任意一个下标 i0 <= i < n),并翻转 s[i] 的值:

  • 如果 s[i] == '1',则将其改为 '0'
  • 如果 s[i] == '0',则将其改为 '1'

目标

通过最多 numOps 次操作,最小化字符串 s最长相同子字符串的长度。相同子字符串指的是子字符串中的所有字符都相同。

示例

示例 1
  • 输入: s = "000001", numOps = 1
  • 输出: 2
  • 解释:
    • s[2] 改为 '1',得到 s = "001001"
    • 此时,最长的相同子字符串为 s[0..1]s[3..4],长度均为 2
示例 2
  • 输入: s = "0000", numOps = 2
  • 输出: 1
  • 解释:
    • s[0]s[2] 改为 '1',得到 s = "1010"
    • 所有相同子字符串的长度均为 1
示例 3
  • 输入: s = "0101", numOps = 0
  • 输出: 1
  • 解释:
    • 不进行任何操作,字符串中所有相同子字符串的长度均为 1

本题重要概念

段的分割

在优化字符串以最小化最长相同子字符串长度的过程中,段的分割是关键步骤。这里的“分割”不仅仅是简单地将字符串切开,而是通过翻转特定位置的字符,将一个连续的相同字符段划分为多个较小的段,从而减少最长段的长度。

具体来说,假设有一个长度为 k 的连续相同字符段。通过翻转其中的若干字符,可以将这个段分割成 seg + 1 个子段。每次翻转一个字符,相当于在该位置引入一个不同字符,从而将原来的段划分为两个部分。例如:

示例:考虑一个包含 10 个连续 ‘0’ 的字符串 “0000000000”。
如果我们翻转其中的 2 个 ‘0’ 为 ‘1’,例如将索引 3 和 7 位置的 ‘0’ 翻转为 ‘1’,字符串变为 “0001000100”。
这样,原本的 10 个 ‘0’ 被分割成了三个子段:“000”, “000”, 和 “00”。
经过这次分割,最长的 ‘0’ 子段长度由 10 减少到了 3。
通过这种方式,每次翻转操作都能有效地将一个较长的段分割成多个较短的段,从而逐步减少整个字符串中最长相同子字符串的长度。具体来说,一个长度为 k 的段,经过 seg 次分割后,每个子段的最大长度大约为 ceil(k / (seg + 1))。

解题思路

要最小化字符串中最长相同子字符串的长度,可以通过以下步骤进行优化:

  1. 检查是否可以分割为长度为1的子串

    • 首先判断是否可以通过最多 numOps 次翻转操作将字符串 s 转换为一个交替字符串,如 "1010""0101"
    • 如果可以实现,则返回 1,因为此时最长的相同子字符串长度为 1
  2. 处理长度为2的字符串

    • 对于长度为2的字符串,如果无法转换为交替字符串(即已经是 "00""11"),则不需要进一步分割。
    • 因为分割长度为2的字符串会影响前后部分,且前面已经判断了是否可以得到长度为1的结果。
    • 如果当前最长的子串长度已经是2,则答案必定是2。
  3. 分段统计

    • 将字符串 s 分割成连续的相同字符段。例如,"000001" 可以分为 ["00000", "1"]
  4. 优先处理最长段

    • 通过优先将最长的相同字符段进行分割,可以有效减少最长段的长度。具体而言,使用优先队列(堆)来不断选择当前最长的段进行分割。
  5. 段的分割

    • 每次分割一个段,将其分成更多的小段,从而降低该段的最大长度。
    • 假设一个段的长度为 k,通过翻转特定位置的字符,将这个段分割seg次,成 seg + 1 个子段,每段的最大长度约为 ceil(k / (seg + 1))
  6. 迭代操作

    • 重复上述过程,直到用完所有的操作次数 numOps 或者无法进一步优化。
  7. 最终结果

    • 操作完成后,堆顶元素即为当前最长相同子字符串的长度。

代码实现

from heapq import heapify, heapreplace
from itertools import groupbyclass Solution:def minLength(self, s: str, numOps: int) -> int:n = len(s)# 首先检查是否可以通过 numOps 次翻转将字符串转为交替字符串# 交替字符串有两种可能:"0101..." 或 "1010..."# 计算将 s 转换为这两种模式所需的翻转次数ops_to_alternate_0 = sum(1 for i, c in enumerate(s) if c != ('0' if i % 2 == 0 else '1'))ops_to_alternate_1 = sum(1 for i, c in enumerate(s) if c != ('1' if i % 2 == 0 else '0'))min_ops_to_alternate = min(ops_to_alternate_0, ops_to_alternate_1)# 如果可以通过翻转操作将字符串转为交替字符串,则最长相同子字符串长度为1if min_ops_to_alternate <= numOps:return 1# 特殊处理长度为2的字符串if n == 2:# 如果已经是交替字符串,返回1if s[0] != s[1]:return 1# 否则,需要至少1次操作将其分割为交替elif numOps >= 1:return 1else:return 2  # 无法分割,最长相同子字符串长度为2# 分组统计连续相同字符的段groups = [len(list(group)) for _, group in groupby(s)]# 如果分组中所有段的长度均为1,则已经是交替字符串if all(g == 1 for g in groups):return 1# 初始化堆,存储每个段的最大长度及分割次数# 堆中存储 (-最大长度, 原始长度, 分割次数)heap = [(-g, g, 1) for g in groups]heapify(heap)for _ in range(numOps):max_seg, k, seg = heap[0]# 如果当前最长段已经不能进一步分割if max_seg == -2:return 2# 将当前最长段进行一次分割# 新的最大长度为 ceil(k / (seg + 1)),这里用整除代替ceil# 因为 k // (seg + 1) 已经是向下取整,取负数用于最大堆new_max = -(k // (seg + 1))# 更新分割次数new_seg = seg + 1# 替换堆顶元素heapreplace(heap, (new_max, k, new_seg))# 返回操作后的最长相同子字符串长度return -heap[0][0]

http://www.ppmy.cn/embedded/148378.html

相关文章

Qt笔记-Qt Creator开发环境搭建

背景 最近参与了一个新项目&#xff0c;与C相关的开发环境是VS2012Qt5.5.1等等。说句真实的&#xff0c;VS开发工具开发纯C/C的项目还是十分方便的&#xff0c;但如果是Qt项目&#xff0c;QtCreator感觉还是略胜一筹。所以准备单独搭建个QtCreator使得后期开发速度嘎嘎的快。 …

【C语言】代码BUG排查方式

【C语言】代码BUG排查方式 文章目录 [TOC](文章目录) 前言一、BUG复现二、printf三、仿真器断点调试1.清除所有断点2.进入调试模式3.打断点&#xff0c;执行 四、参考资料总结 前言 使用工具&#xff1a; 1.ARM仿真器/J-OBV2仿真器 提示&#xff1a;以下是本篇文章正文内容&am…

c# 不同数据类型转换

namespace Systempublic static class ConvertExtension {public static byte[] ToBinaryByteArray(this byte[] bytes){// 每个字节有 8 位&#xff0c;所以总位数为 bytes.Length * 8byte[] binaryArray new byte[bytes.Length * 8];int index 0;// 遍历每个字节foreach (b…

windows 钉钉缓存路径不能修改 默认C盘解决方案

1.问题背景 windows系统C盘被钉钉缓存占用大量空间&#xff0c;导致C盘存储严重不足&#xff1b;但钉钉不支持修改缓存路径 2.解决方案 为钉钉缓存路径创建软连接到其他目录 3.解决步骤 a.钉钉设置里找到&#xff0c;钉钉缓存路径 C:\Users\admin\AppData\Roaming\DingTalk …

初学stm32 --- 外部中断

目录 STM32 IO 口中断基础知识 相关库函数&#xff1a; 使用 IO 口外部中断的一般步骤 STM32 IO 口中断基础知识 STM32 的每个 IO 都可以作为外部中断的中断输入口。STM32F103 的中断控制器支持 19 个外部中断/事件请求。每个中断设有状态位&#xff0c;每个中断/事件都有独立…

百度飞桨:零基础入门深度学习

目录 前言一、概念&#xff1a;机器学习&深度学习1. 机器学习2. 深度学习 二、实操&#xff1a;波士顿房价预测任务1. 线性回归模型2. 线性回归模型的神经网络结构3. 数据处理4. 模型设计5. 训练配置6. 训练过程6.1. 梯度下降法6.2. 计算梯度6.3. 使用Numpy进行梯度计算6.4…

【服务器】linux服务器管理员查看用户使用内存情况

【服务器】linux服务器管理员查看用户使用硬盘内存情况 1、查看所有硬盘内存使用情况 df -h2、查看硬盘挂载目录下所有用户内存使用情况 du -sh /public/*3、查看某个用户所有文件夹占用硬盘内存情况 du -sh /public/zhangsan/*

宠物用品电子商务系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…