mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1356 字
4 分钟
气球的最大数量
2026-06-22

题目描述#

题目链接

给你一个字符串 text,你可以使用其中的字符来组成尽可能多的单词 "balloon"

给你一个字符串 text,计算并返回能够拼成 "balloon"最大数量

输入#

字符串 text

输出#

能拼出的 "balloon" 最大数量

约束条件#

  1. 1 <= text.length <= 10^4
  2. text 仅由小写英文字母组成

示例1#

input: text = "nlaebolko"
output: 1

解释:用 text 中的字符可以拼出一个 "balloon"

示例2#

input: text = "loonbalxballpoon"
output: 2

解释:可以拼出两个 "balloon",剩余字符无法继续拼出第三个。

示例3#

input: text = "leetcode"
output: 0

解释:text 中没有字母 b,无法拼出任何 "balloon"

解题思路#

题目要求用 text 中的字符拼出尽可能多的 "balloon"。每个字符只能使用一次,且拼出的单词必须恰好是 "balloon"

从直观做法出发#

最容易想到的是:在 text 里反复「找齐」一组 b-a-l-l-o-o-n,凑齐就 ans += 1,直到再也凑不齐为止。但每凑一组都要在剩余字符里重新扫描、匹配,实现繁琐,也容易在重复字母上出错。

换一个角度:与其模拟「一组一组地拼」,不如先问——拼一个 "balloon" 到底需要哪些字母、各要几个? 答案一旦固定,整道题就变成了「资源够不够」的问题。

拆解单词 balloon#

"balloon" 的字母拆开统计:

字母balon
出现次数11221

也就是说,每拼出 1"balloon",就要消耗 1b1a2l2o1n

统计字母频次#

因此第一步很直接:遍历 text,统计每个字母出现了多少次。Python 里用 Counter(text) 一行即可完成。

text = "loonbalxballpoon" 为例,统计结果为:

字母balon其他
频次22442x: 1, p: 1

木桶原理:取最小值#

有了各字母的存量,能拼几个 "balloon" 取决于最紧缺的那类字母——就像木桶能装多少水,取决于最短的那块板。

每种字母能「支撑」的 balloon 数量如下:

  • b:每个 balloon 用 1 个 → 最多 cnt['b']
  • a:每个 balloon 用 1 个 → 最多 cnt['a']
  • l:每个 balloon 用 2 个 → 最多 cnt['l'] // 2
  • o:每个 balloon 用 2 个 → 最多 cnt['o'] // 2
  • n:每个 balloon 用 1 个 → 最多 cnt['n']

最终答案就是上述五个值中的最小值

ans=min(cnt[’b’], cnt[’a’], cnt[’l’]//2, cnt[’o’]//2, cnt[’n’])\text{ans} = \min\big(\text{cnt['b']},\ \text{cnt['a']},\ \text{cnt['l']} // 2,\ \text{cnt['o']} // 2,\ \text{cnt['n']}\big)

继续以上面的例子:min(2, 2, 4//2, 4//2, 2) = min(2, 2, 2, 2, 2) = 2,与示例输出一致。

Counter 对不存在的键会返回 0(例如 text = "leetcode" 中没有 bcnt['b']0),因此 min 自然得到 0,无需额外判空。

复杂度#

  • 时间复杂度:O(n)O(n)nntext 长度,统计字母频次需遍历一次
  • 空间复杂度:O(1)O(1),至多统计 26 个小写字母

代码#

from collections import Counter
class Solution:
def maxNumberOfBalloons(self, text: str) -> int:
cnt = Counter(text)
return min(cnt['b'], cnt['a'], cnt['l'] // 2, cnt['o'] // 2, cnt['n'])

解题思路(只统计相关字母)#

上一节用 Counter(text) 统计了 text所有字母的频次。但拼 "balloon" 只会用到 balon 这五个字母——text 里其余的字符(如示例中的 xp)对答案没有任何贡献。

为何可以忽略无关字母#

拼单词时,多出来的字母既不能被「存起来」给下次用,也不会参与任何一次 "balloon" 的组成。因此统计阶段只需关心这五个目标字母,其余字符完全可以跳过。

这样做的好处是:

  • 思路上更清晰:问题被精确归结为「五种资源的配额」;
  • 常数更小:不必为 26 个字母都维护计数(尤其当 text 很长、字母种类很多时)。

单次遍历,按需累加#

维护一个长度为 5 的计数器(或用字典映射),遍历 text 时,若当前字符属于 {b, a, l, o, n} 才累加,否则直接跳过。遍历结束后,同样用木桶原理取 min 即可。

text = "leetcode" 为例,五个相关字母的计数全为 0min 结果为 0;以 text = "nlaebolko" 为例,统计得 b=1, a=1, l=2, o=2, n=1min(1, 1, 1, 1, 1) = 1

逻辑与上一节完全一致,区别仅在于「统计谁」——从「全体字母」收窄为「目标字母」。

代码优化#

Counter 写法已经足够简洁正确。若希望在代码层面进一步精简或避免依赖 collections,可以从以下方向优化。

用固定需求表 + 循环代替手写 min#

把每种字母的需求写进字典,用循环统一计算「该字母最多能支撑几个 balloon」,避免在 return 里重复写五个参数:

class Solution:
def maxNumberOfBalloons(self, text: str) -> int:
need = {'b': 1, 'a': 1, 'l': 2, 'o': 2, 'n': 1}
cnt = {c: 0 for c in need}
for c in text:
if c in cnt:
cnt[c] += 1
return min(cnt[c] // need[c] for c in need)

字母种类固定为 5,循环开销可忽略;若日后单词变化,只需改 need 字典,扩展性更好。

用长度为 26 的数组代替字典#

字母均为小写,ord(c) - ord('a') 可作下标,用整型数组计数,访问比字典略快:

class Solution:
def maxNumberOfBalloons(self, text: str) -> int:
cnt = [0] * 26
for c in text:
cnt[ord(c) - ord('a')] += 1
return min(cnt[ord(c) - ord('a')] // need
for c, need in [('b', 1), ('a', 1), ('l', 2), ('o', 2), ('n', 1)])

渐近复杂度仍为 O(n)O(n) 时间、O(1)O(1) 空间,适合对常数敏感的场景。

小结#

写法优点适用场景
Counter + 手写 min最短、最易读日常刷题、面试快速作答
需求表 + 循环易扩展、逻辑集中单词组成可能变化时
数组计数常数更小对性能有要求的实现

本题数据规模 n104n \le 10^4,三种写法差距极小;首选 Counter 版本即可,其余作为代码风格与扩展性的备选。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

气球的最大数量
https://www.hygen.red/posts/dsa/solution/012气球的最大数量/
作者
Hygen
发布于
2026-06-22
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录