目录

题目描述

171. Excel 表列序号

题意分析

给一个只含大写字母的列名字符串,返回它在 Excel 里对应的列序号:A 是 1,B 是 2,Z 是 26,AA 是 27,AB 是 28,依此类推。

约束里有几个值得注意的信号:输入只含 AZ,既没有小写字母也不会是空串,所以不需要写任何字符合法性校验;长度最多 7;题目保证结果落在 32 位有符号整数范围内,也就是用 int 累加就够,不必升到 long

真正要看清的是这套编号的特殊之处——它没有代表 0 的字符。Z 之后不是 AA 对应 26,而是 27;ZZ 是 702 而不是 675。所以不能照搬「字符减去起点得到一个从 0 开始的数位」这种常规写法,那样每一位都会少 1,而且缺位越多误差越大。

边界包括:单字符输入(AZ)、进位密集的全 Z 情形(ZZ 是 702、ZZZ 是 18278),以及最长的 7 字符输入 FXSHRXW,它的结果恰好是 2147483647,正好顶在 int 上界。

解法:按 26 进制累加

核心思路

Excel 列名是没有数字 0 的 26 进制AZ 分别表示 1 到 26,而不是 0 到 25。若按位展开,AB 就是 $1 \times 26 + 2$;实现时无需显式计算幂,只要像十进制读数一样从左往右累加:

\[ans = ans \times 26 + value\]

其中 value = c - 'A' + 1。循环不变量是:处理完任意前缀后,ans 恰好是该前缀对应的列序号。读入新字符前先乘 26,相当于让已有前缀整体左移一位,再把当前字符填入最低位;因此处理完整个字符串后,ans 就是答案。

解题步骤

  • 初始化 ans = 0,表示尚未读入字符的空前缀。
  • 从左到右遍历列名,将当前字符映射为 1 到 26。
  • 执行 ans = ans * 26 + value,持续维护“ans 等于已读前缀序号”的不变量。
  • 遍历结束后返回 ans

ZY 为例:读入 Zans = 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

易错点总结

  • 忘记 + 1A 会被映射成 0,AA 也会错误地得到 0,而不是 27。
  • 从右往左遍历却仍套用同一递推:AB 会按 BA 的权重计算,得到 53 而不是 28。
  • 写成 ans + 26 * value:乘 26 的对象应是已有前缀,不是当前数位。
  • 使用 Math.pow 再强转整数:引入了不必要的浮点运算和精度风险,直接做整数乘加即可。

相似题目

题目 难度 考察点
168. Excel 表列名称 简单 序号反解列名
13. 罗马数字转整数 简单 相邻符号比较
12. 整数转罗马数字 中等 贪心拆分面值
405. 数字转换为十六进制数 简单 补码按位取模
67. 二进制求和 简单 逐位进位模拟
8. 字符串转换整数 (atoi) 中等 溢出截断处理