题目描述
思路解析
一句话答案:LeetCode 709 转换成小写字母:逐字符扫,码落在 65 到 90 的大写字母按位或 32(差的正是第 5 位)变小写,其余原样,一趟搞定,时间 O(n)、空间 O(n)。
哪些字符要改,其它的原样吗
给一个字符串 s,把里面的大写字母 A 到 Z 换成对应的小写,其余字符——小写、数字、空格、符号——全部原样保留,返回新串。题面例子 s="Hello" 只有 H 是大写,转成 "hello";s="LOVELY" 六个字母全大写,整串变 "lovely";s="here" 本就全小写,原样返回。范围很干净:认准 A 到 Z 这 26 个,别的都不碰。
对每个字符都 +32,为什么串会乱套
小写字母的 ASCII 码,也就是它在计算机里对应的编号,比同一个大写正好大 32,于是很容易想着对整串逐字符 +32 一了百了。可这样非大写字符全遭殃:空格的码是 32,+32 变成 64 也就是 @;数字 0 的码 48,+32 变成 80 也就是 P;已经是小写的字母再 +32 更会直接冲出字母区。所以 +32 这一步不能无脑做,必须先问一句『它是不是大写』。
大小写在二进制里只差哪一位
把码摊成二进制就看明白了。大写 A 到 Z 是 65 到 90,小写 a 到 z 是 97 到 122,同一个字母的大小写只差第 5 位(值 32)那一个比特:大写这位是 0,小写这位是 1。所以要把大写变小写,只需把第 5 位从 0 置成 1,也就是按位或 32(把二进制第 5 位强行拨到 1),写作 c | 32。对大写字母来说,c | 32 和 c + 32 结果完全一样,因为那一位原本是 0,置 1 和加 32 是同一件事。
判范围、拨那一位、拼回去
落到代码就三步走一趟:从头到尾遍历每个字符 c,先看它的码是不是落在 65 到 90 之间(Python 里直接用 c.isupper() 问,C++、Java 用范围判);是大写就 c | 32 拨出小写,不是就原样留下;把每一步的结果依次拼成新串返回。整条串只扫一遍,看过就不再回头。
Hello 五个字符,逐个判该不该动
拿题面的 "Hello" 走一遍。第 0 个 H,码是 72,落在 65 到 90 里,是大写,该转:72 | 32 得 104,也就是 h(72 + 32 同样是 104)。第 1 个 e,码 101,超过 90 不是大写,原样留 e。第 2 个 l、第 3 个 l,码都是 108,不在范围,原样。第 4 个 o,码 111,同样原样。五个字符依次拼起来就是 "hello",全程一趟扫描。
边界卡 65 到 90,差一位就漏 Z 或错收方括号
范围要卡得死死的,严格 65 到 90。要是把上界写成 91,方括号 [(码 91)会被误当字母 +32 改成花括号;把下界写成 64,@(码 64)也会跟着遭殃。字符表上这几个邻居就贴在字母两端,数错一位当场出错。还有个小误会:以为 | 32 和 + 32 是两码事——对大写字母它俩完全等价,放心用哪个都行。
复杂度上,每个字符只看一次,时间 O(n),n 是串长;结果得装进一条等长的新串,空间 O(n)。三种边界心里先跑一遍最稳:"LOVELY" 全大写、整串都要转,"here" 全小写、一个都不动,还有只剩一个字符的串,照样是判范围再决定。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这把尺子:大写小写在 ASCII 表里成对排列,每对正好差 32。下面每一帧都在套「在范围就 +32,不在就放过」。
先把整条串摆出来。大写字母 A、C、E、G、I 夹在小写 b、d、f、h、j 中间。我们从第 0 个开始,一个一个过。
轮到第 0 个字符 「A」,它的 ASCII 码是 65。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「A」 是大写,该转。给它的码 65 加上 32(用位运算 | 32 一步到位)得到 97,也就是小写 「a」。第 0 格变绿,表示这一格已经转好了。
轮到第 1 个字符 「b」,它的 ASCII 码是 98。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「b」 的码 98 不在 65 到 90 这一段,它本来就是小写,什么都不用做,原样留下。第 1 格标蓝,表示看过、未改。
轮到第 2 个字符 「C」,它的 ASCII 码是 67。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「C」 是大写,该转。给它的码 67 加上 32(用位运算 | 32 一步到位)得到 99,也就是小写 「c」。第 2 格变绿,表示这一格已经转好了。
轮到第 3 个字符 「d」,它的 ASCII 码是 100。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「d」 的码 100 不在 65 到 90 这一段,它本来就是小写,什么都不用做,原样留下。第 3 格标蓝,表示看过、未改。
轮到第 4 个字符 「E」,它的 ASCII 码是 69。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「E」 是大写,该转。给它的码 69 加上 32(用位运算 | 32 一步到位)得到 101,也就是小写 「e」。第 4 格变绿,表示这一格已经转好了。
轮到第 5 个字符 「f」,它的 ASCII 码是 102。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「f」 的码 102 不在 65 到 90 这一段,它本来就是小写,什么都不用做,原样留下。第 5 格标蓝,表示看过、未改。
轮到第 6 个字符 「G」,它的 ASCII 码是 71。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「G」 是大写,该转。给它的码 71 加上 32(用位运算 | 32 一步到位)得到 103,也就是小写 「g」。第 6 格变绿,表示这一格已经转好了。
轮到第 7 个字符 「h」,它的 ASCII 码是 104。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「h」 的码 104 不在 65 到 90 这一段,它本来就是小写,什么都不用做,原样留下。第 7 格标蓝,表示看过、未改。
轮到第 8 个字符 「I」,它的 ASCII 码是 73。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「I」 是大写,该转。给它的码 73 加上 32(用位运算 | 32 一步到位)得到 105,也就是小写 「i」。第 8 格变绿,表示这一格已经转好了。
轮到第 9 个字符 「j」,它的 ASCII 码是 106。先问:这个码落在 65 到 90 之间吗,也就是它是不是大写字母?
「j」 的码 106 不在 65 到 90 这一段,它本来就是小写,什么都不用做,原样留下。第 9 格标蓝,表示看过、未改。
十个字符全部过了一遍。绿色的 5 个是从大写转来的,蓝色的 5 个本就是小写、原样保留,拼起来就是答案 abcdefghij。整个过程一遍扫描、没有回头。
三种极端:全小写不动、全大写全转、单字符。先在脑子里跑一遍最稳。
两个高频追问,核心是讲清「差 32 的那一位比特」。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def toLowerCase(self, s: str) -> str: return "".join([chr(ord(c) | 32) if c.isupper() else c for c in s])复杂度
- 时间:O(n),每个字符只看一次,n 是串长
- 空间:O(n),需要一个等长的结果串(字符串通常不可原地改);C++ 传值副本上原地改也是 O(n)
易错点
面试追问把动画讲成自己的话
追问为什么大写转小写可以用位运算 | 32?
追问如果不让用库函数 tolower,怎么写最稳?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
宝石与石头
LeetCode 771 · 简单 · 沿着 数组套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题