解码方法

题目描述

一条包含字母 A-Z 的消息通过以下映射进行了编码:

1
2
3
4
5
"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 位的整数。

示例

示例 1:

1
2
3
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

1
2
3
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6)。

示例 3:

1
2
3
输入:s = "06"
输出:0
解释:"06" 无法映射到 "F",因为存在前导零("6" 和 "06" 并不等价)。

提示

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零

解题思路

这是一道经典的动态规划问题。

核心思想

定义 dp[i] 表示:字符串 s[0:i](即前 i 个字符)的解码方法总数。

我们需要分析每一步的状态转移。

状态分析

  1. 单个字符解码:如果 s[i-1] 不是 '0',则它可以单独解码为一个字母
    $$dp[i] += dp[i-1]$$

  2. 两个字符组合解码:如果 s[i-2:i] 形成的两位数在 10 到 26 之间,则可以组合解码
    $$dp[i] += dp[i-2]$$

特殊情况

  • '0' 字符不能单独解码,必须与前一个字符组合
  • 前导零 "0x"(x 为数字)是无效的

遍历顺序

从左到右遍历字符串,确保在计算 dp[i] 时,dp[i-1]dp[i-2] 已经计算完成。

代码实现

1
2
3
4
5
6
7
8
9
10
class Solution:
def numDecodings(self, s: str) -> int:
dp = [1] + [0] * len(s)
for i in range(1, len(s)+1):
if s[i-1] != '0':
dp[i] += dp[i-1]
if i > 1 and s[i-2] != '0' and int(s[i-2:i]) <= 26:
dp[i] += dp[i-2]

return dp[len(s)]