1 中文题目
一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
"1" -> 'A'
"2" -> 'B'
...
"25" -> 'Y'
"26" -> 'Z'
然而,在 解码
已编码的消息时,有许多不同的方式来解码,因为有些编码被包含在其它编码当中(“2” 和 “5” 与 “25”)。
例如,“11106” 可以映射为:
- “AAJF” ,将消息分组为 (1, 1, 10, 6)
- “KJF” ,将消息分组为 (11, 10, 6)
- 消息不能分组为 (1, 11, 06) ,因为 “06” 不是一个合法编码(只有 “6” 是合法的)。
注意,可能存在无法解码的字符串。
给定一个只含数字的 非空
字符串 s
,请计算并返回 解码
方法的 总数
。如果没有合法的方式解码整个字符串,返回 0
。
题目数据保证答案肯定是一个 32 位
的整数。
示例:
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
输入:s = "06"
输出:0
解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)。
提示:
- 1 ≤ s . l e n g t h ≤ 100 1 \leq s.length \leq 100 1≤s.length≤100
s
只包含数字,并且可能包含前导零。
2 求解方法:动态规划
2.1 方法思路
方法核心
使用动态规划的思想但优化了空间复杂度,只用两个变量代替数组,通过检查单个字符和两个字符的组合是否有效来累计可能的解码方法数,每次更新时只保留最近的两个状态,从而得到最终的解码方法总数。
实现步骤
(1)初始化阶段
- 检查输入的有效性
- 初始化状态变量n1和n2
- 确保起始状态的正确性
(2)状态转移过程
- 单字符检查:
- 检查当前字符是否为’0’
- 若不为’0’,则可以单独解码
- 将前一个状态的解码数累加
- 双字符检查:
- 检查当前字符和前一个字符的组合
- 判断是否在10-26的范围内
- 将前两个状态的解码数累加
- 状态更新:
- 更新前两个状态的值
- 保持最新的解码方法数
- 检查是否出现无法解码的情况
(3)边界条件处理
- 处理空字符串
- 处理以’0’开头的情况
- 处理中间出现无法解码的情况
(4)结果返回
- 返回最终的解码方法数
- 处理特殊情况返回0
方法示例
以 s = "226"
为例:
初始状态:
s = "226"
n1 = 1
n2 = 1
第一次循环 i = 1, 处理'2':
检查单字符:'2' != '0'
temp += n1 = 1
检查双字符:"22" <= "26"
temp += n2 = 1
temp = 2
更新:n2 = 1, n1 = 2
第二次循环 i = 2, 处理'6':
检查单字符:'6' != '0'
temp += n1 = 2
检查双字符:"26" <= "26"
temp += n2 = 1
temp = 3
更新:n2 = 2, n1 = 3
最终结果:3
解码方式:
1. "2" "2" "6" -> "BBF"
2. "22" "6" -> "VF"
3. "2" "26" -> "BZ"
2.2 Python代码
class Solution:
def numDecodings(self, s: str) -> int:
# 如果字符串为空或以'0'开头,无法解码
if not s or s[0] == '0':
return 0
# n1表示dp[i-1],n2表示dp[i-2]
n1 = 1 # 初始化dp[0]=1
n2 = 1 # 用于计算,初始值不重要
# 从第二个字符开始遍历
for i in range(1, len(s)):
# 临时保存当前位置的解码方法数
temp = 0
# 检查单个字符是否可以解码(1-9)
if s[i] != '0':
temp += n1
# 检查两个字符是否可以解码(10-26)
if '10' <= s[i-1:i+1] <= '26':
temp += n2
# 更新n2和n1,向前移动
n2 = n1
n1 = temp
# 如果当前位置无法解码,直接返回0
if n1 == 0:
return 0
return n1
2.3 复杂度分析
- 时间复杂度:O(n)
- 只需要遍历字符串一次
- 每个位置的计算是O(1)
- 空间复杂度:O(1)
- 只使用了常数个变量
- 不需要额外的数组空间
3 题目总结
题目难度:中等
数据类型:字符串
应用算法:动态规划