leetcode1189 木桶短板定产量,Counter 计数展风光

🎮木桶短板定产量,Counter 计数展风光

今天,村长又双叒叕发布了个所谓的“简单日常任务”——说是野外刷新了一批字母史莱姆,这玩意儿打死之后不掉金币,只疯狂掉落小写字母碎片。只要集齐一套 “balloon” 的碎片,就能搓出一个气球炸弹,用来炸开村东头那个卡了三个版本的传送门。

“小白啊,背包 text 里已经攒了一堆碎片了,赶紧算算能搓几个炸弹。”村长叼着烟斗,头也不抬地疯狂画饼。

我默默打开背包,看着里面那串乱码一样的字符串:“nlaebolko”。好家伙,这字母碎片堆得,简直像极了抽卡游戏里满屏的R级狗粮。

“这还不简单?”我挑了挑眉,嘴角勾起一抹“基操勿6”的自信微笑,“村长,您这任务,不就是个经典的木桶效应嘛!木桶能装多少水,取决于最短的那块木板;我能合成多少次炸弹,取决于哪个字母最先被榨干!在编程界,这叫取最小值(Min),在管理学里,这叫‘寻找系统瓶颈’!”

我心里暗暗吐槽:村长这老头,怕不是偷偷去B站刷了《管理学原理》的速成网课,学了个木桶理论就跑来给我当策划了?啧,理论一套套,代码写不好,说的就是他这种人。

“要拼一个 ‘balloon’,配方是:1个 b、1个 a、2个 l、2个 o、1个 n。首先,我得给 text 做个全量扫描,统计每个字母的库存,然后找出最少的那个——”

“手动写个字典遍历统计?太 low 了,简直是原始人行为!”我反手掏出一块散发着赛博朋克蓝光的魔法计数板(俗称 collections.Counter),在村长面前晃了晃:

🔧 传说级道具:Counter 来源:官方collections 模块 功能:自动扫描可迭代对象,瞬间统计每个元素的爆率(次数),并返回一个字典-like 的对象 效果:一行代码,优雅计数,时间复杂度稳稳压在 O(n),告别 O(n²) 的龟速遍历!

“一行代码,搞定计数!“我潇洒地敲下:

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

等等——如果 text 里根本没有某个字母会怎么样? 比如 text=“leetcode”,里面就没一个 ‘b’ 数。我试了一下:cnt[‘b’] 会返回 0!因为 Counter 的特性是访问不存在的键时返回 0 而不是报错!所以 min(0, …) 的结果就是 0,正好完美契合“缺一个字母就白搭”的逻辑。

我一拍大腿,“ 这才是真正的’内置容错外挂’!连异常处理都省了!python官方是懂我们的!”

我对着背包里的碎片,露出了奸商般的微笑——把 “nlaebolko” 传进去——cnt[‘b’]=1, cnt[‘a’]=1, cnt[’l’]//2=1, cnt[‘o’]//2=1, cnt[’n’]=1,取最小值得 1。 “完美!刚好搓 1 个炸弹。”

村长又丢来一串更长的:“loonbalxballpoon”。我重新调用:b=2, a=2, l=4//2=2, o=4//2=2, n=2,取最小值得 2。 “稳了,两个炸弹!”

村长点点头,但嘴角带着一丝狡黠:“那要是以后让你合成 “apple” 呢?你不得重新写个函数?”

我愣了一下,“村长,您这属于典型的‘需求变更’ !我这刚写完 “balloon” ,您转头就要 “apple”,这搁我们开发圈,得加钱的!”

但我很快稳住了阵脚,冷笑一声:““真以为我是那种写死配置的脚本小子吗?格局打开,咱们直接上配置驱动!”

我深吸一口气,开始向村长展示什么叫真正的“高内聚低耦合”:

“把目标字符串也丢给 Counter 处理,直接拿到配方字典!然后,遍历这个配方字典,用背包里的库存除以配方需求量,最后再取个全局 min。这样一来,别说合成 'apple',你就是让我搓个 'abracadabra',我也能一行代码给你算出来!”

def maxNumberOfBalloons(text: str, word: str) -> int:
    cnt_word = Counter(word)
    cnt_text = Counter(text)
    return min(cnt_text[char] // cnt_word[char] for char in word)

“看到没?生成器表达式加字典遍历,优雅,丝滑,毫无冗余!”我自信地瞅了眼村长。

结果村长吧嗒吧嗒抽了两口烟斗,沉默了一会儿,憋出一句:“哦,懂了。那如果背包是空的呢?”

我愣住了——如果cnt_text[ch] 访问不到就直接返回 0,0 // cnt_word[ch] 还是 0,min 里面有一个 0,整个结果就是 0。不会报错啊!

“村长,“我眯起眼睛,往前凑了半步,“您这是在给我做边界测试呢?”

村长嘿嘿一笑:“不,我是在教你,什么叫防御性编程。你以为这就完了?万一哪天系统升级,字母史莱姆掉落的碎片里混进了大写字母,或者带空格、带标点符号,你的 Counter 还能认出来吗?”

我:“……”好家伙,这是从算法题直接跳到数据清洗了是吧?

我咬咬牙,把代码又往上叠了一层:“ 加层过滤!先来个 .lower() 统一转小写,再来个 filter(str.isalpha, …) 把非字母字符全扬了,最后再丢给 Counter!这样总行了吧?上到国际通用字母,下到emoji,全部能处理!”

村长满意地点点头,把烟斗往鞋底上磕了磕:“不错,孺子可教。不过……”

“不过什么?!”我感觉脑门上突突的。

“不过村东头那个传送门,”村长指了指远处,“其实不是卡BUG了。是上个月我我嫌密码太简单,改成了 ‘abracadabra’,结果转头就忘了……” 村长继续说道:“你刚才这么一说,我忽然想起来密码是啥了。”

村长又补了句:“你搓的这几个炸弹也别浪费,拿去村西头的池塘炸点小鱼吧,最近鱼有点多。“

我默默地把 Counter 收回背包,深吸一口气,微笑着说:“村长,您知道在计算机领域,把简单问题复杂化,然后假装自己解决了什么大难题的人,一般叫什么吗?”

村长:“叫什么?”

我:“架构师。”

村长:“……滚。”


这正是:

气球合成别蛮干,Counter 计数展风光。

木桶短板定产量,村长一言全白忙。

知识共享许可协议
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。