LeetCode 补充题 161. 有符号三进制字符串转十进制
题目描述
给定可带正负号的三进制整数字符串
s,将它转换成任意精度的十进制字符串。只允许数字
0、1、2,非法输入抛出异常或返回错误。
示例 1:
输入:
s = "-102"
输出:"-11"
解释: 三进制 102 表示1×3²+0×3+2=11,保留负号。
示例 2:
输入:
s = "+002"
输出:"2"
解释: 前导正号和零不影响整数值,十进制结果为 2。
示例 3:
输入:
s = "1203"
输出:解析失败
解释: 数字 3 不属于三进制;Java 抛出解析异常,Go 返回失败标志。
提示:
- 仅允许一个可选的前导正负号及数字
0、1、2。 - 结果为任意精度十进制字符串。
- 非法输入返回解析失败。
题意分析
输入只允许 ASCII 数字 0、1、2,以及一个可选的前导正负号。手撕时逐位计算 value=value×3+digit;任意精度的乘加和十进制格式化交给标准库。Java 非法输入抛出解析异常,Go 返回空串与 false。
解法:逐位乘三累加并处理符号
核心思路
[!blue]
value始终表示已经读取的三进制数字前缀的非负数值。新来一位时,旧前缀的每个数位权重都提高三倍,再加当前数字,得到value = value * 3 + digit。逐位应用这一式子就是完整的位权展开。只在开头读取一次可选符号,符号后至少保留一位数字。之后每个字符都必须是 ASCII
0、1、2,不能忽略空格或中间符号;遇到非法字符立即返回解析失败。全程用大整数累积,最后统一应用负号并转成十进制字符串。前导零自然不改变数值,负零也规范化为
0;不能先用固定宽度整数接收输入再转大整数。
解题步骤
- 检查非空输入,读取至多一个前导符号;只有符号时失败。
- 逐字符检查 0、1、2,执行 value=value×3+当前位。
- 最后应用负号,并由大整数库输出十进制字符串。
代码实现
class Solution {
public String ternaryToDecimal(String s) {
if (s == null || s.isEmpty()) {
throw new NumberFormatException("empty input");
}
int index = 0;
boolean negative = false;
if (s.charAt(0) == '+' || s.charAt(0) == '-') {
negative = s.charAt(0) == '-';
index++;
}
if (index == s.length()) {
throw new NumberFormatException("missing digits");
}
BigInteger value = BigInteger.ZERO;
BigInteger base = BigInteger.valueOf(3);
for (; index < s.length(); index++) {
char digit = s.charAt(index);
if (digit < '0' || digit > '2') {
throw new NumberFormatException("invalid ternary digit");
}
value = value.multiply(base).add(BigInteger.valueOf(digit - '0'));
}
return (negative ? value.negate() : value).toString();
}
}
import "math/big"
func ternaryToDecimal(s string) (string, bool) {
if len(s) == 0 {
return "", false
}
index := 0
negative := false
if s[0] == '+' || s[0] == '-' {
negative = s[0] == '-'
index++
}
if index == len(s) {
return "", false
}
value, base := new(big.Int), big.NewInt(3)
for ; index < len(s); index++ {
digit := s[index]
if digit < '0' || digit > '2' {
return "", false
}
value.Mul(value, base)
value.Add(value, big.NewInt(int64(digit-'0')))
}
if negative {
value.Neg(value)
}
return value.String(), true
}
复杂度分析
- 时间复杂度:解析执行 $O(n)$ 次大整数乘小常数与加法,数值位数随输入长度增长,不能把每次大整数运算都当作固定代价。十进制输出的转换成本另计。
- 空间复杂度:随位数为 $O(n)$。
关键点总结
[!green]
手撕的是位权递推与输入规则,大整数乘加复用标准库;不能用固定宽度整数暂存完整输入。
易错点总结
[!yellow]
- 不能先把长输入转成 int 或 long,再构造大整数。
- 只有符号不是合法数字;符号只能出现在第一位。
- 逐位校验 ASCII 0、1、2,非法字符不能被忽略;负零输出为 0。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 补充题 145. 有符号整数的进制转换 | 中等 | 两题是相反方向:本题从高位累乘解析,另一题从低位取余生成数位。 |
| 171. Excel 表列序号 | 简单 | 同样按位权累乘累加,Excel 列数字从 1 开始,本题数码从 0 开始且使用任意精度。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!