91. 解码方法(中等)

Blind 75 › 91. 解码方法(中等) (第 6 章)
文章工具

题目

一条由大写字母 A–Z 组成的消息,被逐字符编码成数字:1 表示 A,2 表示 B,……,26 表示 Z。现在给你一个只含数字的字符串 s,你需要把它切分成若干段,每段必须落在 1 到 26 之间(所以两位码只有 10–26,06 不是合法编码)。请计算共有多少种不同的切分方法;如果整串无法合法解码,返回 0。

示例 1
输入:s = "121"
输出:3
解释:可切为 "1,2,1"(ABA)、"12,1"(LA)、"1,21"(AU)。

示例 2
输入:s = "210"
输出:1
解释:只能切为 "2,10"(BJ);"21,0" 与 "2,1,0" 都不合法,因为 0 不能单独成码,而 21 后面只剩一个 0。

示例 3
输入:s = "301"
输出:0
解释:首位 3 单独成码后剩下 "01",0 不能作为一段的开头;"30" 又超过 26,因此无合法方案。

约束
1 ≤ s.length ≤ 100;s 只包含数字字符,可能以 0 开头;答案保证在 32 位有符号整数范围内。

思路

一句话本质:从第一个数字走到末尾,每步可选「走 1 个字符」或「走 2 个字符」,但每步落下的片段必须落在合法码 1–26 上,这是一个带合法性判断的路径计数问题。

最直白的做法:在每个位置做选择——要么把当前字符单独切开,要么把它和后一个字符一起切开;遇到不合法的片段就回溯。最坏情况下每个位置都有两种选择,搜索树接近满二叉树,时间复杂度为 O(2n)。而 n 最大可达 100,这个规模完全无法承受。

关键观察:假设我们已经知道「前 i-1 个字符」的方案数 dp[i-1] 和「前 i-2 个字符」的方案数 dp[i-2],那么看当前前缀 s[0..i-1]:

  • 若最后一位 s[i-1] 落在 1–9,它可单独成码,方案来自 dp[i-1];
  • 若最后两位 s[i-2..i-1] 落在 10–26,它们可一起成码,方案来自 dp[i-2]。

两种来源互不重叠,直接相加即可。而 dp[i] 只依赖 dp[i-1] 与 dp[i-2] 两个前状态,重叠子问题非常明显,因此可以线性扫一遍。

做法时间复杂度空间问题
暴力回溯O(2n)O(n)同一前缀被反复计算
记忆化搜索O(n)O(n)可行,但需要递归栈
自底向上 DPO(n)O(n),可优化到 O(1)最直接,推荐

选定做法:自底向上 DP,令 dp[i] 表示前 i 个字符的合法解码方案数,按上述转移从 i=2 推到 i=n。

解法

按四步实现:

  1. 定义 dp[i] 为 s 前 i 个字符的合法解码方案数。
  2. 初始化 dp[0] = 1(空串只有一种空方案);若 s[0] == '0',直接返回 0,否则 dp[1] = 1。
  3. 从 i=2 到 n 转移:单字符 1–9 合法则加 dp[i-1];双字符 10–26 合法则加 dp[i-2]。
  4. 返回 dp[n]。

以 s = "121" 走一遍:

i末尾单字符末尾双字符dp[i]
1‘1’ 合法,取 dp[0]—1
2‘2’ 合法,加 dp[1]“12”

🔒

本篇为付费内容

您已阅读试读部分,购买后即可解锁全文。

⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。

还没有评论,来说两句?

发表评论

欢迎交流技术细节;广告与人身攻击会被删除。

在线咨询