LeetCode 423 - 从英文中重建数字


文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 代码解析
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
这道题《LeetCode 423. 从英文中重建数字(Reconstruct Original Digits from English)》挺有意思。
它不是那种常规的数值运算题,而更像是一个“单词拼图”挑战。
题目把一些英文数字单词(zero, one, two, …, nine)打乱顺序混合在一起,我们要根据这些字母重新推算出原始的数字序列。
看起来有点像解谜游戏,其实考的就是“字符串计数 + 特征提取”的能力。
这种题型在自然语言处理、OCR后文本修复、甚至语音识别转写矫正中都有类似的逻辑。

描述
给定一个字符串 s,其中包含被打乱顺序的英文单词表示的数字(zero 到 nine)。
要求我们按升序返回这些数字的字符串表示。
例如:
输入: s = "owoztneoer"
输出: "012"
解释: "zero" + "one" + "two" 的字母混合在一起。
再比如:
输入: s = "fviefuro"
输出: "45"
解释: 包含 "four" + "five"
题目还保证输入一定能组成合法的数字单词集合,不用考虑无法解析的情况。
题解答案
解决这道题的关键在于:每个数字单词都有独特的字母特征。
比如:
- “z” 只出现在 “zero” 中,所以每出现一个 “z”,就一定有一个 “0”。
- “w” 只出现在 “two” 中。
- “u” 只出现在 “four” 中。
- “x” 只出现在 “six” 中。
- “g” 只出现在 “eight” 中。
这些独特字符让我们可以先识别出部分数字。
然后再根据这些数字中已用掉的字母,反推出剩下的数字。
举个例子:
- “h” 出现在 “three” 和 “eight” 中,但如果先算出 “eight”,剩下的 “h” 只能来自 “three”。
- “f” 出现在 “four” 和 “five” 中,去掉 “four” 后剩下的 “f” 就属于 “five”。
所以整个过程其实是一个按特征顺序剥洋葱的过程。

题解代码分析
下面是完整可运行的 Swift 代码:
import Foundationclass Solution {func originalDigits(_ s: String) -> String {// 1. 统计每个字母的出现次数var count = [Character: Int]()for c in s {count[c, default: 0] += 1}// 2. 用数组存每个数字出现的次数var res = [Int](repeating: 0, count: 10)// 3. 按独特字符特征判断数字res[0] = count["z"] ?? 0 // zerores[2] = count["w"] ?? 0 // twores[4] = count["u"] ?? 0 // fourres[6] = count["x"] ?? 0 // sixres[8] = count["g"] ?? 0 // eight// 4. 推导剩余数字res[3] = (count["h"] ?? 0) - res[8] // threeres[5] = (count["f"] ?? 0) - res[4] // fiveres[7] = (count["s"] ?? 0) - res[6] // sevenres[1] = (count["o"] ?? 0) - res[0] - res[2] - res[4] // oneres[9] = (count["i"] ?? 0) - res[5] - res[6] - res[8] // nine// 5. 组合结果var ans = ""for i in 0..<10 {ans += String(repeating: "\(i)", count: res[i])}return ans}
}// Demo 测试
let s = Solution()
print("测试1:", s.originalDigits("owoztneoer")) // 输出 "012"
print("测试2:", s.originalDigits("fviefuro")) // 输出 "45"
代码解析
-
计数字母:用一个字典统计每个字母出现的次数。
-
识别独特字符:
"z"对应0"w"对应2"u"对应4"x"对应6"g"对应8
-
再推剩余的:
因为这些数字会“消耗”掉某些字母,所以我们减去它们的数量,再得出其他数字的数量。 -
结果拼接:按从小到大的顺序拼接数字字符串。
这段代码的逻辑非常清晰,也贴近实际字符串统计的套路,完全可以迁移到类似文本频率计算的业务场景中。
示例测试及结果
示例 1:
输入: s = "owoztneoer"
输出: "012"
解释:包含 “zero”, “one”, “two”,重新排列后输出 012。
示例 2:
输入: s = "fviefuro"
输出: "45"
解释:包含 “five”, “four”,升序排列后输出 45。
执行结果:
测试1: 012
测试2: 45
代码输出与预期一致。
时间复杂度
O(N)
每个字符只统计和访问一次,后续推导都是常数操作,因此整体线性时间复杂度。
即使字符串长度达到 10⁵ 级别,也完全没问题。
空间复杂度
O(1)
因为英文字母表大小固定,数字种类也固定,所以空间使用不随输入增长。
严格来说我们用了一些字典和固定数组,但总体为常数级。
总结
这道题的核心是“找到独特特征字符”这一思路,非常有启发性。
它看似是纯算法题,其实背后逻辑和自然语言特征提取、符号消歧、模式识别等领域非常接近。
在实际开发中,如果遇到:
- OCR 识别出的混乱文本;
- 用户输入中带有模糊词;
- 或日志关键词识别中多个重叠的特征词;
都可以借鉴类似的“频率减法”思路:
先确定独特特征,再逐步剥离剩余部分,最终恢复完整结构。
Swift 在处理这类计数逻辑时的写法也非常简洁——利用字典的默认值和 String(repeating:count:) 拼接方式,整个流程直观又易读。
