Python 字符串为什么不可变:从 replace 到变量重绑定

Python 字符串为什么不可变:从 replace 到变量重绑定 副标题: replace 看起来修改了字符串,实际发生的是创建结果并让变量改指向它。理解这一区别,才能看懂字符串方法、别名行为和循环拼接的成本。 适读人群: 刚开始学习 Python 字符串、变量与对象关系的读者 阅读时间: 6 分钟 从一段括号消除代码说起 下面的函数反复删除成对的括号,直到字符串不再变化: def is_valid_by_elimination(s: str) -> bool: while True: previous = s s = s.replace("()", "").replace("[]", "").replace("{}", "") if len(s) == len(previous): return s == "" 这里容易产生一个疑问: previous = s s = s.replace("()", "") 既然 previous 和 s 原来指向同一个字符串,第二行为什么不会同时改变 previous? 答案是:Python 字符串是不可变对象。replace 没有修改原字符串,而是计算出一个 结果,随后赋值语句让变量 s 改为指向这个结果。previous 仍然指向原字符串。 “不可变”到底是什么意思 字符串不可变,指的是: 一个 str 对象创建以后,它所表示的字符序列不能被原地修改。 例如: text = "cat" text[0] = "b" Python 会抛出异常: TypeError: 'str' object does not support item assignment 不能把已有字符串中的 c 原地改成 b。如果需要 "bat",必须得到另一个字符串值: ...

2026年8月28日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 20:有效的括号,为什么数量相同仍然无效

题目要求 给定一个只包含以下六种字符的字符串 s: ( ) { } [ ] 判断这个字符串是否有效。有效字符串必须同时满足三个条件: 每个左括号都由相同类型的右括号闭合。 左括号必须按照正确顺序闭合。 每个右括号都有一个对应的同类型左括号。 满足全部条件时返回 True,否则返回 False。 示例 输入 输出 "()" True "()[]{}" True "(]" False "([])" True "([)]" False 约束 1 <= s.length <= 10^4 s 只包含 ()[]{} 中的字符 LeetCode 提供的方法签名是: class Solution: def isValid(self, s: str) -> bool: pass Step 1:数量相同为什么仍然无效 先看两个字符串: ()[]{} ([)] 它们都有一个 ( 和一个 )、一个 [ 和一个 ]。如果只分别统计三种左括号和右括号的数量,这两个字符串都会通过检查。 当前 baseline 是: 分别统计每种左括号和右括号;数量全部相同就认为字符串有效。 这个 baseline 会在 ([)] 上给出错误答案。问题不在括号数量,而在闭合顺序:读到一个右括号时,它必须闭合最近遇到、但还没有被闭合的左括号。 把这个规则用于 ([)]: ...

2026年8月21日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 394:字符串解码,如何保存并恢复嵌套上下文

题目要求 给定一个编码字符串 s,返回它解码后的字符串。 编码规则是: k[encoded_string] 方括号中的 encoded_string 需要连续重复 k 次,其中 k 是正整数。编码可以嵌套,也可以与普通小写字母相邻。 题目保证: 输入字符串始终有效,方括号完整配对且没有多余空格。 原始文本不包含数字,数字只表示重复次数。 不会出现 3a 或 2[4] 这类不符合编码规则的输入。 解码后的字符串长度不会超过 10^5。 示例 输入 输出 "3[a]2[bc]" "aaabcbc" "3[a2[c]]" "accaccacc" "2[abc]3[cd]ef" "abcabccdcdcdef" 约束 1 <= s.length <= 30 s 只包含小写英文字母、数字和 [] 所有重复次数都在 [1, 300] 范围内 LeetCode 提供的方法签名是: class Solution: def decodeString(self, s: str) -> str: pass Step 1:进入内层以后,外层信息去了哪里 先从没有嵌套的输入开始: 3[a] 读到 3 后知道下一段需要重复三次;读完方括号中的 a,得到: a * 3 = aaa 当前 baseline 是: 读出一个重复次数,再收集后续方括号中的文本,遇到 ] 时执行重复。 这个 baseline 可以处理一层 3[a],却会在 3[a2[c]] 上中断。外层已经读到重复次数 3 和普通字母 a,此时又遇到了内层编码 2[c]。如果直接把当前次数改成 2、当前文本改成 c,外层的 3 和 a 就丢失了。 ...

2026年8月21日 · 4 分钟 · map[name:Jeanphilo]

LeetCode 208:实现 Trie(前缀树)模板题解析

副标题 / 摘要 208 题的难点不在算法变化,而在把 Trie 模板按平台接口写稳:insert 建路径,search 查完整单词,startsWith 只查前缀路径。 预计阅读时长:8~10 分钟 标签:Hot100、Trie、前缀树、LeetCode 208 SEO 关键词:LeetCode 208, Implement Trie, Prefix Tree, startsWith 元描述:从接口要求出发实现 LeetCode 208,讲清 Trie 节点、children、is_end、insert/search/startsWith 的区别。 A — Algorithm(题目与算法) 先看最小操作压力 208 题最关键的操作序列是: Trie trie = new Trie() trie.insert("apple") trie.search("apple") -> true trie.search("app") -> false trie.startsWith("app") -> true trie.insert("app") trie.search("app") -> true 这个例子说明: app 可以是 apple 的前缀 但只有插入过 app 后,search("app") 才能返回 True 所以这题不是“路径存在就算命中”。 我们必须同时维护: 路径是否存在 这条路径是否刚好是完整单词 题目接口 设计一个 Trie,也叫前缀树,支持三个操作: ...

2026年6月24日 · 3 分钟 · map[name:Jeanphilo]

Trie 模板:从节点字段到插入查询 invariant

副标题 / 摘要 Trie 的重点不是背代码,而是理解“一个节点代表一个前缀”。只要这个模型稳定,插入、完整单词查询和前缀查询都会变成同一个循环。 预计阅读时长:8~10 分钟 标签:Hot100、Trie、前缀树、字典树 SEO 关键词:Trie, 前缀树, 字典树, children, is_end 元描述:用 Python 写一个最小 Trie 模板,讲清节点字段、children 走法、结束标记和循环 invariant。 A — Algorithm(从一个小任务开始) 小任务:同时回答完整单词和前缀 假设已经插入: app apple bat 现在要问: app 是不是完整单词? ap 是不是某个单词的前缀? apply 是否存在? 这个小任务暴露了两个缺口: 只用哈希集合,可以快速判断完整单词,但不能自然回答前缀问题 只看路径存在,又会把 app 和 apple 的前缀关系混成一件事 Trie 要解决的就是:让很多字符串共享公共前缀,同时还能区分“前缀存在”和“完整单词存在”。 从压力反推要支持什么 我们先不管任何题目接口,只定义一个自己的模板: insert(word):把一个单词插入 Trie search(word):判断完整单词是否存在 starts_with(prefix):判断是否存在以 prefix 开头的单词 最小结构图 插入 app 和 apple 后,结构可以想成: root └─ a └─ p └─ p [end] └─ l └─ e [end] 这里最重要的是: ...

2026年6月24日 · 3 分钟 · map[name:Jeanphilo]

Hot100:分割回文串(Palindrome Partitioning)回溯 + 回文预处理 ACERS 解析

副标题 / 摘要 131. 分割回文串 的难点不是递归本身,而是两个问题要同时想清楚:当前切到了哪里,以及一个候选片段是不是回文。把这两件事拆开,你就能得到“回溯枚举 + 回文表预处理”的稳定写法。 预计阅读时长:15~18 分钟 标签:Hot100、回溯、字符串、回文、DP SEO 关键词:Palindrome Partitioning, 分割回文串, 回溯, 回文预处理, DP 元描述:通过 LeetCode 131 理解字符串分割型回溯与回文区间预处理,掌握“先判合法片段,再递归切后缀”的题目模型。 A — Algorithm(题目与算法) 题目还原 给定一个字符串 s,请把它切分成若干个子串,使得每个子串都是回文串,并返回所有可能的切分方案。 输入输出 名称 类型 描述 s string 只包含小写英文字母的字符串 返回 string[][] 所有合法的回文切分方案 示例 1 输入:s = "aab" 输出:[["a","a","b"],["aa","b"]] 示例 2 输入:s = "a" 输出:[["a"]] 约束 1 <= s.length <= 16 s 仅由小写英文字母组成 目标读者 已经学过 78/90 这类数组回溯,准备进入“字符串切分型回溯”的学习者 会写递归,但总把“切分位置”和“合法性检查”搅在一起的开发者 想掌握“先预处理合法区间,再枚举切分”的工程迁移套路的读者 背景 / 动机 这题非常适合帮你建立一种更通用的模型: 先判断哪些区间是合法片段,再递归地把整个串切完。 如果你只是把它看成“回文题”,容易只盯着 isPalindrome()。 但如果你把它看成“字符串分割题”,就会更清楚它的结构: ...

2026年4月19日 · 10 分钟 · map[name:Jeanphilo]

Hot100:电话号码的字母组合(Letter Combinations of a Phone Number)固定层数 DFS ACERS 解析

副标题 / 摘要 这题表面上像字符串题,实质上是一个非常标准的固定层数回溯模型:第 k 层只处理第 k 个数字,从其映射字母里选一个,直到路径长度等于输入长度。 预计阅读时长:10~12 分钟 标签:Hot100、回溯、字符串、DFS SEO 关键词:Letter Combinations of a Phone Number, 电话号码的字母组合, 回溯, DFS 元描述:用 LeetCode 17 建立固定层数 DFS 模板,理解字符映射、路径长度终止与多语言实现。 A — Algorithm(题目与算法) 题目还原 给定一个仅由数字 2 到 9 组成的字符串 digits,返回它能表示的所有字母组合。 答案顺序不限。数字与字母的映射与电话按键一致,1 不对应任何字母。 输入输出 名称 类型 描述 digits string 由 2 到 9 组成的数字串 返回 string[] 所有可能的字母组合 示例 1 输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"] 示例 2 输入:digits = "2" 输出:["a","b","c"] 提示 1 <= digits.length <= 4 digits[i] 是范围 ['2', '9'] 内的一个数字 目标读者 已经掌握 78 / 46,准备看另一类回溯树形态的学习者 想把“每层处理一个位置”这种 DFS 模型固定下来的开发者 需要做编码扩展、短串生成、候选串组合的工程师 背景 / 动机 这题和子集、排列都不太一样。 ...

2026年4月2日 · 7 分钟 · map[name:Jeanphilo]

LeetCode 76:最小覆盖子串(Minimum Window Substring)滑动窗口 ACERS 解析

副标题 / 摘要 最小覆盖子串是“可变滑动窗口 + 计数哈希表”的经典题。本文按 ACERS 模板解释如何判断窗口有效、如何收缩得到最短答案,并给出工程场景与多语言实现。 预计阅读时长:12~15 分钟 标签:滑动窗口、哈希表、字符串 SEO 关键词:Minimum Window Substring, 最小覆盖子串, 滑动窗口, 哈希表 元描述:最小覆盖子串的 O(n) 滑动窗口解法与工程应用,含多语言实现。 目标读者 正在刷 LeetCode 的中级开发者 需要掌握“可变窗口 + 覆盖约束”的算法模板 做文本分析、日志聚合或流式过滤的工程师 背景 / 动机 “在一段序列中找到最短区间覆盖目标集合”在工程中非常常见: 日志告警需要覆盖多种错误码,搜索摘要需要覆盖关键字, 运营分析需要覆盖多个行为标签。 本题提供了一个可复用的窗口收缩模板。 核心概念 可变滑动窗口:右指针扩张直到满足条件,左指针收缩缩短答案 计数哈希表:支持重复字符,必须按次数覆盖 满足条件的计数:判断当前窗口是否“覆盖了全部需要” A — Algorithm(题目与算法) 题目重述 给定字符串 s 和 t,返回 s 中最短的子串,使其包含 t 中的每一个字符(包括重复字符)。 若不存在这样的子串,返回空字符串 ""。 测试用例保证答案唯一。 输入输出 名称 类型 描述 s string 源字符串 t string 目标字符串(需要覆盖的字符与次数) 返回 string 最短覆盖子串或空串 示例 1 s = "ADOBECODEBANC", t = "ABC" 输出 = "BANC" 示例 2 s = "a", t = "a" 输出 = "a" 示例 3 s = "a", t = "aa" 输出 = "" C — Concepts(核心思想) 方法类型 可变滑动窗口 + 频次覆盖判断。 ...

2026年1月20日 · 9 分钟 · map[name:Jeanphilo]

LeetCode 1456:最大元音子串数量的滑动窗口 ACERS 解析

副标题 / 摘要 最大元音子串数量是“固定窗口计数”的标准模板题。本文按 ACERS 结构讲清楚滑动窗口的核心思想,并给出工程场景与多语言实现。 预计阅读时长:10~12 分钟 标签:滑动窗口、字符串、固定窗口 SEO 关键词:Maximum Number of Vowels, 最大元音子串, 滑动窗口, 固定窗口 元描述:滑动窗口求固定长度子串最大元音数,含工程化应用与多语言代码。 目标读者 正在刷 LeetCode / Hot100 的同学 想建立“固定窗口计数”模板的中级开发者 需要做日志/指标窗口统计的工程师 背景 / 动机 固定长度窗口内的最大计数是工程里极常见的需求: 监控系统统计异常峰值、运营分析统计活跃峰值、NLP 统计特征峰值。 如果每次窗口都重新计算,会退化为 O(nk)。 滑动窗口能让每步更新变成 O(1),把整体降到 O(n)。 核心概念 固定滑动窗口:窗口长度固定为 k,只右移一位 增量更新:进入右端元素、移除左端元素 条件计数:只统计满足条件(本题为元音)的元素数量 A — Algorithm(题目与算法) 题目重述 给你一个字符串 s 和整数 k。 返回长度为 k 的子串中,元音字符数量的最大值。 输入输出 名称 类型 描述 s string 只包含小写英文字符 k int 窗口长度 返回 int 任意长度为 k 的子串中最大元音数 示例 1 s = "abciiidef", k = 3 输出 = 3 示例 2 s = "aeiou", k = 2 输出 = 2 C — Concepts(核心思想) 方法类型 固定滑动窗口 + 条件计数。 ...

2026年1月20日 · 6 分钟 · map[name:Jeanphilo]

LeetCode 1513:仅含 1 的子串数量(连续 1 子串计数)ACERS 解析

副标题 / 摘要 这是“连续 1 子串计数”的标准题:用 cur 维护以当前位置结尾的连续 1 长度即可在线累加答案。本文按 ACERS 模板给出清晰模型、工程场景与多语言实现。 预计阅读时长:10~12 分钟 标签:计数、字符串、连续段 SEO 关键词:Number of Substrings With Only 1s, 连续1子串, LeetCode 1513 元描述:在线统计连续 1 子串数量的 O(n) 解法与工程化应用。 目标读者 正在刷 LeetCode / 准备面试的同学 想建立“连续段计数”模板的中级开发者 做日志分析、监控与行为统计的工程师 背景 / 动机 “只含 1 的连续子串数量”看似简单,但它对应一类非常常见的工程统计: 连续事件强度、稳定性评分、连续活跃天数、心跳连续正常等。 掌握这题等于掌握“连续段贡献计数”的可复用模型。 核心概念 连续子串:必须连续,不能跳过元素 连续段(run):一段连续的 1 在线累加(cur 模型):记录以当前位置结尾的连续 1 长度 取模:答案可能很大,需要取 1e9+7 A — Algorithm(题目与算法) 题目重述 给你一个二进制字符串 s,请返回 仅由字符 ‘1’ 组成的子串 的数量。 子串要求连续且非空。 输入输出 名称 类型 描述 s string 只包含 ‘0’ 和 ‘1’ 返回 int 仅含 1 的子串数量(取模) 示例 s = "0110111" 输出 = 9 解释:连续 1 段为长度 2 和 3,贡献分别为 3 和 6,总和 9。 ...

2026年1月18日 · 5 分钟 · map[name:Jeanphilo]