青训1_1105_03 最小替换子串长度

请添加图片描述

.md

文章目录

  • 请添加图片描述
    • 一 问题描述
      • 测试样例
      • 示例
    • 二 思路
      • 个人思路(ERROR)
      • 思路(right)
    • !!解题思路 详细
    • 答案
    • 三、理解
    • 1、 理解嵌套循环:也就是连续子串的所有可能性位置-看懂了
      • 2、问题又来了,即使确定了能得到不同长度连续子串,也有不同种变换方式。这是又是怎么处理的?_答:不具体考虑替换成啥,只考虑截取字段数量能否满足外界替换需求量(因为题目只要求找到最短子串,而不要求提供具体替换子串长啥样,只要数学上存在可行性,这个子串就列入可选)

一 问题描述

小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。

测试样例

样例1:

输入:input = "ADDF"
输出:1

样例2:

输入:input = "ASAFASAFADDD"
输出:3

样例3:

输入:input = "SSDDFFFFAAAS"
输出:1

样例4:

输入:input = "AAAASSSSDDDDFFFF"
输出:0

样例5:

输入:input = "AAAADDDDAAAASSSS"
输出:4

示例

def solution(input):# Please write your code herereturn -2if __name__ == "__main__":#  You can add more test cases hereprint(solution("ADDF") == 1 )print(solution("ASAFASAFADDD") == 3)

二 思路

个人思路(ERROR)

完全没想法

思路(right)

疑问:替换字母,让最终每个字母出现次数一样,所以不明白啥是最小子串。
答:需要连续替换字母,而不是单独替换(区别DNA序列编辑位置)
关键点在于:替换必须是连续的字母
要找到能让整个字符串平衡的最短连续子串
如果原字符串已经平衡,返回0
如果无法通过替换达到平衡,返回-1

!!解题思路 详细

在这里插入图片描述

让我一步步解释解题思路是如何推导出来的。

  1. 首先分析问题的特点:
    • 要让所有字母出现次数相等
    • 我们要找的是一段连续的子串去替换
    • 需要找最短的符合条件的子串
  2. 从简单情况开始思考:ok
# 例如: "ADDF"
# 当前统计:A(1次), D(2次), F(1次), S(0次)
# 要让每个字母出现1次,显然只需要把一个D改成S就行
  1. 然后归纳解题步骤

    1、具体解题思路分解

# 1. 先写一个函数判断字符串是否平衡
def is_balanced(s):freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}for c in s:freq[c] += 1# 如果所有字符出现次数相等,返回Truereturn len(set(freq.values())) == 1# 2. 如果已经平衡,直接返回0
if is_balanced(input_str):return 0# 3. 否则,从长度1开始尝试:
for length in range(1, len(input_str) + 1):# 对于每个长度,尝试所有可能的起始位置for start in range(len(input_str) - length + 1):# 判断替换这段子串是否能达到平衡

2、关键点是怎么判断一个子串是否可以替换:数学可行性

 # 计算每个字符还需要多少个needed = {'A': 0, 'S': 0, 'D': 0, 'F': 0}valid = Truefor char in new_freq:if new_freq[char] > target:  # 如果某个字符太多,无法通过替换达到平衡valid = Falsebreakneeded[char] = target - new_freq[char]# 如果所需字符数等于子串长度,说明可以通过替换达到平衡if valid and sum(needed.values()) == length:return length

3、让我们用一个具体例子走一遍:

# 输入:"ADDF"
# n = 4, target = 1 (每个字符应该出现1次)# 初始频次:
# A: 1
# D: 2
# F: 1
# S: 0# 尝试长度为1的子串:
# 当考虑替换第二个D时:
# - 减去这个D后的频次:A(1), D(1), F(1), S(0)
# - 需要补充:S(1)
# - 子串长度也是1
# 所以这是一个可行解,返回1

这道题的思路核心是:

  1. 它直接模拟了替换过程

    通过计算替换后的频次和所需补充的字符数来判断是否可行

    从最小长度开始尝试,保证找到的是最小解

答案

def solution(input_str):n = len(input_str)target = n // 4  # 因为是4的倍数,所以每个字符应该出现n/4次# 统计每个字符出现的次数freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}for c in input_str:freq[c] += 1# 如果已经平衡,返回0if all(f == target for f in freq.values()):return 0# 从小到大尝试子串长度for length in range(1, n + 1):# 遍历所有可能的起始位置for start in range(n - length + 1):# 尝试替换这个子串后的频次new_freq = freq.copy()# 减去要替换的子串中的字符频次for i in range(start, start + length):new_freq[input_str[i]] -= 1# 检查是否可以通过替换这个子串达到平衡needed = {'A': 0, 'S': 0, 'D': 0, 'F': 0}valid = Truefor char in new_freq:if new_freq[char] > target:  # 如果某个字符频次超过目标值,无法通过替换达到平衡valid = Falsebreakneeded[char] = target - new_freq[char]if valid and sum(needed.values()) == length:return lengthreturn -1if __name__ == "__main__":#  You can add more test cases hereprint(solution("ADDF") == 1 )print(solution("ASAFASAFADDD") == 3)

三、理解

1、 理解嵌套循环:也就是连续子串的所有可能性位置-看懂了

# 3. 否则,从长度1开始尝试:
for length in range(1, len(input_str) + 1):# 对于每个长度,尝试所有可能的起始位置for start in range(len(input_str) - length + 1):# 判断替换这段子串是否能达到平衡

让我用一个具体的例子来解释这段代码的逻辑。

假设我们有输入字符串 "ASAFASAFADDD" (长度为12)

# 外层循环: length从1开始尝试
for length in range(1, len(input_str) + 1):# length = 1, 2, 3, ... , 12

让我们看看当 length = 3 时的内层循环是如何工作的:

让我们详细拆解:

  1. 为什么是 range(len(input_str) - length + 1)?
# 假设 length = 3:
input_str = "ASAFASAFADDD"  # 长度12# range(12 - 3 + 1) = range(10)
# 也就是从0到9的位置都可以作为起始位置
# 为什么到9?因为从位置9开始取3个字符正好到末尾
# 如果从10开始就不够3个字符了# 具体示例:
start = 0:  "ASA"FASAFADDD
start = 1:  A"SAF"ASAFADDD
start = 2:  AS"AFA"SAFADDD
start = 3:  ASA"FAS"AFADDD
...
start = 9:  ASAFASAFA"DDD"
  1. 完整示例代码帮助理解:
def print_all_substrings(input_str, length):for start in range(len(input_str) - length + 1):substring = input_str[start:start + length]print(f"位置{start}: {substring}")# 测试
s = "ASAFASAFADDD"
print("当length=3时,所有可能的子串:")
print_all_substrings(s, 3)

输出会是:

当length=3时,所有可能的子串:
位置0: ASA
位置1: SAF
位置2: AFA
位置3: FAS
位置4: ASA
位置5: SAF
位置6: AFA
位置7: FAD
位置8: ADD
位置9: DDD
  1. 为什么要这样遍历?
    • 我们需要找到一个连续的子串进行替换
    • 这个子串可能在字符串的任何位置
    • 通过这两层循环,我们可以:
      1. 尝试所有可能的子串长度(外层循环)
      2. 对每个长度,尝试所有可能的起始位置(内层循环)
      3. 直到找到一个可以通过替换达到平衡的子串
  2. 在实际解题中:
for length in range(1, len(input_str) + 1):for start in range(len(input_str) - length + 1):# 对于每个子串,检查替换它是否能让整个字符串平衡if can_balance_with_substring(input_str, start, length):return length  # 找到答案就返回

这就像是在一个窗口在字符串上滑动:

  • 先用长度为1的窗口从左到右滑动
  • 如果找不到解,就用长度为2的窗口重新滑动
  • 依此类推,直到找到一个可以替换的子串

这样理解这段代码的作用了吗?它就是在系统地尝试所有可能的"连续子串",看哪一个可以通过替换来达到我们的目标。

2、问题又来了,即使确定了能得到不同长度连续子串,也有不同种变换方式。这是又是怎么处理的?_答:不具体考虑替换成啥,只考虑截取字段数量能否满足外界替换需求量(因为题目只要求找到最短子串,而不要求提供具体替换子串长啥样,只要数学上存在可行性,这个子串就列入可选)

子串替换判断逻辑

在这里插入图片描述

以 “ADDF” 为例:

让我们看看代码是如何处理的:

def solution(input_str):n = len(input_str)target = n // 4  # 目标:每个字符应该出现的次数# 初始频次统计freq = {'A': 0, 'S': 0, 'D': 0, 'F': 0}for c in input_str:freq[c] += 1# 对于 "ADDF":# freq = {'A': 1, 'D': 2, 'F': 1, 'S': 0}# target = 1for length in range(1, n + 1):for start in range(n - length + 1):# 复制一份频次字典,准备模拟替换new_freq = freq.copy()# 减去要替换的子串中的字符for i in range(start, start + length):new_freq[input_str[i]] -= 1# 举例:当length=1, start=1时(第二个位置的D)# 减去这个D后:# new_freq = {'A': 1, 'D': 1, 'F': 1, 'S': 0}# 计算还需要多少个各类字符needed = {'A': 0, 'S': 0, 'D': 0, 'F': 0}valid = Truefor char in new_freq:if new_freq[char] > target:valid = Falsebreakneeded[char] = target - new_freq[char]# 对于上面的例子:# needed = {'A': 0, 'S': 1, 'D': 0, 'F': 0}# sum(needed.values()) = 1,正好等于子串长度# 关键判断:所需字符数是否等于子串长度if valid and sum(needed.values()) == length:return length

关键点在于:

  1. 我们不需要具体知道怎么替换
    • 我们只需要知道"是否可能通过替换达到平衡"
    • 具体替换成什么字符并不重要,只要数学上成立即可
  2. 判断方法:
    • 去掉要替换的子串中的字符
    • 计算还缺少多少个各类字符
    • 如果缺少的字符总数等于子串长度,就说明可以通过替换达到平衡
  3. 举个具体例子
# "ADDF",考虑替换第二个D
原始频次:A(1), D(2), F(1), S(0)
目标频次:都应该是11. 去掉D后的频次:A(1), D(1), F(1), S(0)
2. 计算需要补充的数量:A需要:0个(已经1个)D需要:0个(已经1个)F需要:0个(已经1个)S需要:1个(缺1个)
3. 总共需要1个字符 = 子串长度1说明可以通过替换这个D来达到平衡

所以,我们不需要关心具体的替换方式,只需要:

  1. 确定替换这个位置是否有可能达到平衡
  2. 通过计算频次和所需字符数来判断
  3. 如果数学上可行,就说明一定存在一种替换方式

这种方法的巧妙之处在于:

  • 避免了枚举所有可能的替换方式
  • 只需要证明存在可行解
  • 大大简化了问题的复杂度

用数学方法避免了枚举所有可能的替换方式。

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

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

相关文章

222页PPT集团公司供应链管理SOP计划管理流程规划

S&OP(Sales & Operations Planning),即销售与运营计划,也被称为产销协同,是一种综合性的企业管理方法。以下是对S&OP计划管理流程规划的详细内容: 一、S&OP的基本概念与目的 S&OP是一…

第三十五篇:HTTP报文格式,HTTP系列二

HTTP 是超⽂本传输协议,也就是HyperText Transfer Protocol。 前面我们讲到第三章中网络协议的定义,网络协议的定义:网络协议是通信计算机双方必须共同遵从的一组约定。就像两个人要进行交流,如果不制定一套约定,一方…

华夏教育集团《梦回延安》全国巡演河南站纪实

传承红色精神,推动中国式家校共育。日前,由华夏教育集团太阳谷华夏学校携手河南少年先锋学校、世纪先锋学校联合推出的大型红色舞台剧《梦回延安》在河南省人民会堂精彩亮相。 河南是中华文明的发祥地之一,此次《梦回延安》舞台剧首次走出辽宁…

Idea如何推送项目到gitee

第一步:先在你的gitee创建一个仓库 第二步: 点击推送 点击定义远程,将URL换成你仓库的,填好你的用户名和密码 可以看到已经推送到仓库了

Leecode:977. 有序数组的平方

题目 ——Leecode:977. 有序数组的平方 目录 题目 ——Leecode:977. 有序数组的平方 题目分析 暴力解法: 双指针解法: 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排…

动态规划-两个数组的dp问题——44.通配符匹配

1.题目解析 题目来源:44.通配符匹配——力扣 测试用例 2.算法原理 1.状态表示 本题属于两个数组的dp问题,这里需要使用p中的字符消去s中的字符且p中有特殊字符可以匹配s中的普通字符,属于寻找相同子序列的变式,所以需要一个二维d…

Linux命令 - 目录与文件基本操作

文章目录 1 文件系统树2 几个特殊的目录3 绝对路径与相对路径4 文件系统中跳转与浏览4.1 文件系统中跳转4.2 查看目录内容4.2.1 ls命令详解4.2.2 确定文件类型示例 5 操作目录与文件5.1 强大的通配符5.2 复制目录/文件5.3 移动/重命名目录/文件5.4 删除目录/文件5.5 创建目录5.…

基于STM32的自动化植物浇灌系统教学

引言 随着城市化进程的加快,越来越多的人开始关注家庭园艺与植物养护。基于STM32的自动化植物浇灌系统可以帮助用户在忙碌的生活中顺利管理植物的水分需求。本教学文章将指导您如何利用STM32构建一个简单实用的植物浇灌系统,实现自动浇水功能。 环境准备…

美格智能5G车规级通信模组: 5G+C-V2X连接汽车通信未来十年

自2019年5G牌照发放开始,经过五年发展,我国5G在基础设施建设、用户规模、创新应用等方面均取得了显著成绩,5G网络建设也即将从基础的大范围覆盖向各产业融合的全场景应用转变。工业和信息化部数据显示,5G行业应用已融入76个国民经…

【CRM系统选型指南:国内八大主流工具比较】

本文将对十大主流CRM系统进行比较:纷享销客、Zoho CRM、Pipedrive、简信CRM、HubSpot CRM、八百客CRM、金蝶CRM、浪潮CRM、销售易CRM 本文将深入评比2024年主流的CRM系统,帮助你了解各系统之间的主要区别、优缺点以及当前的发展趋势。通过详细的比较和分…

node.js的exports使用误区解释

exports和module.exports指向同一个对象,最终共享的结果,以module.exports指向的对象为准。 exports 和 module.exports 使用误区 使用require()导入的模块,使用的永远是module.exports指向的对象 实例1 exports.age 23 module.exports {n…

Maven项目的基础配置:利用IDEA将SpringBoot的项目打包成war文件

文章目录 引言Maven项目的聚合与继承(依赖管理)把项目打包成war包其他打包配置引言 利用IDEA将SpringBoot的项目打包成war文件Maven项目的聚合与继承(依赖管理)Maven项目的聚合与继承(依赖管理) 把项目打包成war包 利用IDEA将SpringBoot的项目打包成war文件:要配置启动…

Nuxt.js 应用中的 nitro:config 事件钩子详解

title: Nuxt.js 应用中的 nitro:config 事件钩子详解 date: 2024/11/2 updated: 2024/11/2 author: cmdragon excerpt: nitro:config 是 Nuxt 3 中的一个生命周期钩子,允许开发者在初始化 Nitro 之前自定义 Nitro 的配置。Nitro 是 Nuxt 3 的服务器引擎,负责处理请求、渲…

51c大模型~合集14

我自己的原文哦~ https://blog.51cto.com/whaosoft/11603879 # LLM 结构化数据生成原理 如何结合人工规则让 LLM 输出符合 JSON 格式的数据。 目前 LLM(Large Language Model)从文本补全到内容创作,都展示出了强大的生成能力。然而通过 L…

CSRA的LINUX操作系统24年11月2日下午上课笔记

压缩和解压缩:zip 、gzip、bz、xz # zip 压缩 # 压缩文件夹 # 解压缩 # unzip -v 查看压缩包中的内容 # bzip2 dir1/* :将dir1中的所有文件压缩 # gzip # 压缩文件夹 # 解压缩 tar 归档命令: # 创建tar包 tar -c*f # 释放tar包 tar -xf[c] # c …

MyBatis 返回 Map 或 List<Map>时,时间类型数据,默认为LocalDateTime,响应给前端默认含有‘T‘字符

一、问题 MyBatis 返回 Map 或 List时,时间类型数据,默认为LocalDateTime Springboot 响应给前端的LocalDateTime,默认含有’T’字符,如何统一配置去掉 二、解决方案 1、创建配置类,对ObjectMapper对象进行定制&am…

数据结构算法篇--递归(c语言版)

目录 1.递归 1.1求阶乘: 1.2.斐波那契数 1.3. 求幂 1.递归 在C语言中,递归是一种函数调用自身的方法,用来解决一些具有重复性质的问题。例如,计算阶乘、斐波那契数列等问题都可以通过递归实现。 递归在书写的时候&#xff0…

在vue3的vite网络请求报错 [vite] http proxy error:

在开发的过程中 代理proxy报错: [vite] http proxy error: /ranking/hostRank?dateType1 Error: connect ETIMEDOUT 43.xxx.xxx.xxx:443 网络请求是http的: // vite.config.ts import { Agent } from node:http;server: {host: 0.0.0.0,port: port,open: true,https: false,…

西南科技大学C++作业1——组合依赖关系实验代码

目录 一、实现效果预览 二、实验要求 三、实现代码 book.h book.cpp borrow.h borrow.cpp library.h library.cpp student.h student.cpp main.cpp 一、实现效果预览 二、实验要求 作业1:类与类关系设计(组合或依赖) 目 的: 1) 巩固类的定义,成员变量、成员方…

GPIO子系统中Controller驱动源码分析

往期内容 本专栏往期内容: Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体Pinctrl子系统中c…