LeetCode 171. Excel 表列序号
题目描述
题意分析
给一个只含大写字母的列名字符串,返回它在 Excel 里对应的列序号:
A是 1,B是 2,Z是 26,AA是 27,AB是 28,依此类推。约束里有几个值得注意的信号:输入只含
A到Z,既没有小写字母也不会是空串,所以不需要写任何字符合法性校验;长度最多 7;题目保证结果落在 32 位有符号整数范围内,也就是用int累加就够,不必升到long。真正要看清的是这套编号的特殊之处——它没有代表 0 的字符。
Z之后不是AA对应 26,而是 27;ZZ是 702 而不是 675。所以不能照搬「字符减去起点得到一个从 0 开始的数位」这种常规写法,那样每一位都会少 1,而且缺位越多误差越大。边界包括:单字符输入(
A到Z)、进位密集的全Z情形(ZZ是 702、ZZZ是 18278),以及最长的 7 字符输入FXSHRXW,它的结果恰好是 2147483647,正好顶在int上界。
解法:按 26 进制累加
核心思路
Excel 列名是没有数字 0 的 26 进制:
\[ans = ans \times 26 + value\]A到Z分别表示 1 到 26,而不是 0 到 25。若按位展开,AB就是 $1 \times 26 + 2$;实现时无需显式计算幂,只要像十进制读数一样从左往右累加:其中
value = c - 'A' + 1。循环不变量是:处理完任意前缀后,ans恰好是该前缀对应的列序号。读入新字符前先乘 26,相当于让已有前缀整体左移一位,再把当前字符填入最低位;因此处理完整个字符串后,ans就是答案。
解题步骤
- 初始化
ans = 0,表示尚未读入字符的空前缀。- 从左到右遍历列名,将当前字符映射为 1 到 26。
- 执行
ans = ans * 26 + value,持续维护“ans等于已读前缀序号”的不变量。- 遍历结束后返回
ans。以
ZY为例:读入Z后ans = 26;再读入Y,得到26 * 26 + 25 = 701。
代码实现
class Solution {
public int titleToNumber(String columnTitle) {
int ans = 0;
for (int idx = 0; idx < columnTitle.length(); idx++) {
int value = columnTitle.charAt(idx) - 'A' + 1;
ans = ans * 26 + value;
}
return ans;
}
}
func titleToNumber(columnTitle string) int {
ans := 0
for idx := 0; idx < len(columnTitle); idx++ {
value := int(columnTitle[idx]-'A') + 1
ans = ans*26 + value
}
return ans
}
复杂度分析
- 时间复杂度:$O(n)$,每个字符只处理一次。
- 空间复杂度:$O(1)$,只维护累加结果和当前字符值。
关键点总结
A必须映射为 1;这是 Excel 编号与普通 26 进制的根本区别。- 从高位到低位使用“旧值乘基数再加新位”,可以省去反转、求幂和额外存储。
- 正确性依赖前缀不变量,而不是对样例的规律猜测。
- 题目保证答案在 32 位有符号整数范围内;最长合法样例
FXSHRXW恰好得到2147483647。
易错点总结
- 忘记
+ 1:A会被映射成 0,AA也会错误地得到 0,而不是 27。- 从右往左遍历却仍套用同一递推:
AB会按BA的权重计算,得到 53 而不是 28。- 写成
ans + 26 * value:乘 26 的对象应是已有前缀,不是当前数位。- 使用
Math.pow再强转整数:引入了不必要的浮点运算和精度风险,直接做整数乘加即可。
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 168. Excel 表列名称 | 简单 | 序号反解列名 |
| 13. 罗马数字转整数 | 简单 | 相邻符号比较 |
| 12. 整数转罗马数字 | 中等 | 贪心拆分面值 |
| 405. 数字转换为十六进制数 | 简单 | 补码按位取模 |
| 67. 二进制求和 | 简单 | 逐位进位模拟 |
| 8. 字符串转换整数 (atoi) | 中等 | 溢出截断处理 |