【算法】Decode Ways II 解码方式2

本文深入解析了DecodeWaysII问题,详细阐述了解码方式2的题目背景,提供了高效的解题思路,并附带代码实现。探讨了数字字符串在包含特殊字符'*'时的解码可能性,讲解了如何计算所有可能的解码方式,适用于长度为[1,105]的输入字符串。

【算法】Decode Ways II 解码方式2

题目

A message containing letters from A-Z is being encoded to numbers using the following mapping way:

‘A’ -> 1
‘B’ -> 2

‘Z’ -> 26
Beyond that, now the encoded string can also contain the character ‘*’, which can be treated as one of the numbers from 1 to 9.

Given the encoded message containing digits and the character ‘*’, return the total number of ways to decode it.

Also, since the answer may be very large, you should return the output mod 109 + 7.

Example 1:
Input: “"
Output: 9
Explanation: The encoded message can be decoded to the string: “A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I”.
Example 2:
Input: "1

Output: 9 + 9 = 18
Note:
The length of the input string will fit in range [1, 105].
The input string will only contain the character ‘*’ and digits ‘0’ - ‘9’.

编码方式如下:

  • 1 ~ 26 分别映射 “A” ~ “Z”
  • “*” 可以代指 0 ~ 9

给出一组长度为 [1,105] 的数字字符串,求可以有多少种解码方式。

解题思路

一个数字字符串,如果只考虑 0 ~ 9,没有 * ,则编码方式只有一种,在本题中由于将范围扩大到了 [1,26],且还有 * 插入,所以我们只需要针对字符串中的 1 、2、*进行针对处理即可。
以 6219 为例,62 和 621 分别有 1、2
假设当前字符串 sub[0,n-1] 、 sub[0,n] 有 i、j 种编码方式,若 sub[0,n] 的末尾字符为 1 ,则 sub[0,n+1] 计算时可分为两种情况(sub[0,n+1] 的末尾字符为 subn+1):

  1. subn+1 单独进行解码时,编码方式有 j 种
  2. subn+1 与 subn 进行合并解码时,编码方式有 i 种

所以 sub[0,n+1] = j + i = str[0,n] + str[0,n-1]。

在此基础上需要做一些区分的注意:

  1. 若subn+1 为 0 ,则只能与 subn 进行合并解码,不能单独解码。
  2. subn == 1,2 时,subn+1 在 [0,9],[0,6] 范围内,才能与 subn 进行合并解码。
  3. subn == * 时,由于 * 代指 [0,9],所以 subn+1 单独解码的方式 9 倍,合并解码时, 位数为 1 时解码方式 9 倍,尾数为 2 时解码方式 6 倍。

代码实现

Runtime: 100 ms
Memory: 20.5 MB

func numDecodings(_ s: String) -> Int {
        //字符串一共有多少种解码方式
        var e0:Int64 = 1
        //存储字符串末位为 1 时的编码方式数量
        var e1:Int64 = 0
        //存储字符串末位为 1 时的编码方式数量
        var e2:Int64 = 0
        //临时计算用的容器
        var f0:Int64 = 0
        //模的除数
        let M:Int64 = Int64(1e9 + 7)
        //遍历字符串 s
        for c in s
        {
            if c == "*"
            {
                //由于 * 可以代指 1 ~ 9
                //所以 e0 和 e1 都有九种方式,而最大到 26 所以 e2 只有 6 种
                f0 = 9 * e0 + 9 * e1 + 6 * e2
                //由于 * 可以代指 1 和 2,所以 e1 和 e2 都需要赋值
                e1 = e0
                e2 = e0
            }
            else
            {
                //先计算合并解码的情况
                //当前一位为 1 时,当前位在[0,9]范围内都可以进行合并解码,所以将 e1 加入到容器
                f0 = e1
                //当前一位为 2 时,当前位在[0,6]范围内才可以进行合并解码,将 e2 加入到容器
                if c <= "6"
                {
                    f0 += e2
                }
                
                //计算单独解码
                //如果当前字符为 0 ,则不能进行单独解码
                //所以只有当前位在[1,9]范围内才可以单独解码,将 e0 加入到容器
                if c > "0"
                {
                    f0 += e0
                }
                //依据当前字符是否为 1 或 2 ,来判断是将 e1 e2 为 e0 还是 0
                //以 61616 为例,当循环到 6161 时,当前字符为 1
                //在下次遍历到 6 时,由于会用到 616 和 6161 的结果
                //所以在此次遍历中,将 e0 赋值给 e1
                //这样,e1 中保留的就是 616 的结果,而 6161 的结果可在之后通过 f0 进行计算存储到 e0 中
                //当前字符不为 1 时,则下次遍历不会出现变动,故将 e1 变更为 0
                // e2 也是相同道理
                e1 = c == "1" ? e0 : 0
                e2 = c == "2" ? e0 : 0
            }
            //本次循环中,用 f0 % M 得出结果 e0
            e0 = f0 % M
        }
        //返回结果
        return Int(e0)
    }

代码地址:https://github.com/sinianshou/EGSwiftLearning

在数字序列解码问题中,**最佳解码方式**的自动检测通常需要结合算法设计、统计分析和上下文理解。以下是常见的算法和方法: --- ### **1. 暴力搜索与回溯算法** - **适用场景**:当数字序列可能对应多种编码组合(如ASCII、字母序号、Unicode等)时。 - **实现逻辑**: 尝试所有可能的拆分方式(如1位或2位数字为一组),验证每组数字是否在有效编码范围内(如A=1~26)。 - **优化**:通过剪枝(提前终止无效路径)减少计算量。 - **示例**: ```python def decode_ways(s): dp = {len(s): 1} # 动态规划表 for i in range(len(s)-1, -1, -1): if s[i] == "0": dp[i] = 0 else: dp[i] = dp[i+1] if i+1 < len(s) and 10 <= int(s[i:i+2]) <= 26: dp[i] += dp[i+2] return dp[0] ``` --- ### **2. 动态规划(DP)** - **核心思想**:记录子问题的解,避免重复计算。 - **典型问题**: - 数字串解码为字母(如`"121"` → `"ABA"`, `"AU"`, `"LA"`)。 - 状态转移方程: `dp[i] = dp[i+1] + dp[i+2]`(若当前数字和下一数字组合有效)。 --- ### **3. 统计频率分析** - **适用场景**:已知编码对应字符的频率分布(如英文中E最高频)。 - **方法**: - 统计数字分组后的字符频率,与语言标准频率(如字母表、词频)对比。 - 使用卡方检验或熵值评估匹配度。 --- ### **4. 机器学习分类器** - **步骤**: 1. 提取特征(如数字分组长度、组合有效性、上下文关联性)。 2. 训练模型(如随机森林、神经网络)判断最佳解码方式。 - **优势**:可处理复杂规则(如混合编码、自定义密码本)。 --- ### **5. 基于规则的启发式算法** - **规则示例**: - 优先尝试常见编码(如ASCII、Unicode)。 - 排除无效组合(如`"00"`无对应字母)。 - 检查分隔符(如空格、标点提示分组边界)。 --- ### **6. 概率模型(隐马尔可夫模型/Viterbi算法)** - **适用场景**:序列解码存在概率依赖(如前一数字影响当前分组)。 - **实现**:计算最大概率路径,确定最优解码序列。 --- ### **选择算法的关键因素** | 因素 | 推荐算法 | |--------------------|----------------------------| | 编码规则明确 | 动态规划、回溯法 | | 需频率统计支持 | 卡方检验、熵分析 | | 复杂或混合编码 | 机器学习、规则引擎 | | 实时性要求高 | 启发式规则+剪枝 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值