LeetCode 91. Decode Ways

本文深入探讨了使用动态规划解决字符串编码问题的方法。通过详细解释两种解码方式,即从前向后和从后向前,作者展示了如何通过动态规划函数dp来计算解码方法的数量。同时,文章提供了C++代码实现,清晰地展示了如何通过遍历字符串并考虑字符之间的组合来求解。此外,还介绍了动态规划方法在实际应用中的优势和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

There are two ways to solve this problem. One is to solve it front-back, the other is back-front.

I currently only wrote the back-front method:

Suppose we have such a string:    a0, a1, a2.... ai, ai+1, ai+2...an-1

Suppose we have get that from an-1, an-2...ai+2, the ways of decoding such a sequence is dp[i+2]

There are three conditions to consider:

1: if ai == 1, we dont need to care about ai+1, the ways to decode is 1+ dp[i+2];

2: if ai == 2, ai+1 should be smaller than '6', the ways to decode is 1 + dp[i+2].

3: if ai == 2, ai+1 greater than '6', the ways to decode is dp[i+1].

Think that, decode a individual number is 1, but 0 has no way to decode, thus, 0.

So, we can have the dynamic function:  dp[i] = dp[i] + dp[i+2] if(s[i] == 1 || s[i] == 2 && s[i + 1] < '6')

#include <string>
#include <vector>
#include <iostream>
using namespace std;

/*
  A message containing letters from A-Z is being encoded to numbers using the following mapping:
  'A' --> 1
  'B' --> 2
  ....
  'Z' --> 26
  Given an encoded message containing digits, determine the total number of ways to decode it.
  For example:
  Given encoded message "12", it could be decoded as "AB" or "L". 
  The number of ways decoding "12" is 2.
*/
/*
  Analyze: 
  Several example will make this question clear.
  "102" --> "10", "2" -> total method is 1
  "12" --> {"1", "2"}, "12" --> total method is 2.
  "127" --> {"1", "2", "7"}, {"12", "7"} --> total method is 2
  "126" --> {"1", "2", "6"}, {"12", "6"}, {"1", "26"}--> total method is 3.
  There are several cases need special attentions.
  1: "1"/"2" + "0", the char "0" can be interpreted as a decode way.
  2: "2" + "7, 8, 9" can only be interpreted as one way.
*/

int numDecodings(string s) {
  int n = s.size();
  vector<int> dp(n+2, 1);
  for(int i = s.size() - 1; i >= 0; --i) {
    if(s[i] == 0) dp[i] = 0;
    else dp[i] = dp[i + 1];

    if(i + 1 < s.size() && (s[i] == '1' || (s[i] == '2' && s[i+1] <= '6'))) {
      dp[i] += dp[i+2];
    }
  }
  return dp[0];
}



int main(void) {
  cout << "123" << " decode ways: "<< numDecodings("123") << endl;
  cout << "102" << " decode ways: "<< numDecodings("102") << endl;
  cout << "127" << " decode ways: "<< numDecodings("127") << endl;
}


To visualize the problem.... Everytime, we can decode two chars or one chars.


'0' is a special case to case, there are two cases that we might encounter '0'.  However, no matter where we have 0, the corresponding decode way will 0.

#include <string>
#include <vector>
#include <iostream>
using namespace std;
// decode it from back to front.
int decodeWays(string str, int len) {
  if(len == 0 || len == 1) return 1;
  int num = 0;
  if(str[len - 1] > '0') // this line should be commented out! for the case "10000000" == 1
    num = decodeWays(str.substr(0, len - 1), len - 1);
  if(str[len - 2] == '1' || ((str[len - 2] == '2') && str[len - 1] < '6'))
    num += decodeWays(str.substr(0, len - 2), len - 2);
  return num;
}

// dp
int decodeWaysII(string str) {
  int n = str.size();
  vector<int> dp(n + 1, 0);
  dp[0] = dp[1] = 1;
  for(int i = 2; i <= n; ++i) {
    if(str[i - 1] > '0') {
      dp[i] = dp[i-1];
    }
    if(str[i-2] == '1' || (str[i-2] == '2' && str[i-1] < '6'))
      dp[i] += dp[i-2];
  }
  return dp[n];
}

// to simplify more
int decodeWaysIII(string str) {
  int n = str.size();
  if(n <= 1) return 1;
  int prev = 1;
  int curr = 1;
  int sum = 0;
  for(int i = 2; i <= n; ++i) {
    if(str[i-1] == '0') prev = 0;
    if(str[i-2] != '1' && !(str[i-2] == '2' && str[i-1] < '6'))
      curr = 0;
    sum = curr + prev;
    prev = curr;
    curr = sum;
  }
  return curr;
} 


Another ways is more cool! and space efficient!

// In-place ways
int decodeWays(string str) {
  if(str[0] == '0') return 0;
  int prev = 0;
  int curr = 1;
  for(int i = 1; i <= str.size(); ++i) {
    if(s[i-1] == '0') curr = 0;
    if(i < 2 || !(s[i-2] == '1' || (s[i-2] == '2' && s[i-1] <= '6')))
      prev = 0;

    int tmp = curr;
    curr = curr + prev;
    prev = tmp;
  }
  return curr;
}

reference: http://www.acmerblog.com/leetcode-solution-decode-ways-6209.html

Another variation: Asked by Amazon,  return all the decoded strings, better to use backtracking, the time complexity is T(n) = T(n-1) + T(n-2) Thus it is 2^N

#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
using namespace std;

void decodeWays(string str, int pos, string path, vector<string>& res, unordered_map<string, char>& dict) {
  if(pos > str.size()) return;
  if(pos == str.size()) {
    res.push_back(path);
    return;
  }
  for(int i = 1; i <= 2; ++i) {
    string tmp = str.substr(pos, i);
    if(dict.count(tmp)) {
      decodeWays(str, pos + i, path + dict[tmp], res, dict);
    }
  }
}

vector<string> decodeWays(int input, unordered_map<string, char>& dict) {
  string str = to_string(input);
  if(str.size() == 0) return {};
  vector<string> res;
  string path = "";
  decodeWays(str, 0, path, res, dict);
  return res;
}
int main(void) {
  unordered_map<string, char> dict {
    {"1", 'A'},
    {"2", 'B'},
    {"3", 'C'},
    {"4", 'D'},
    {"5", 'E'},
    {"6", 'F'},
    {"7", 'G'},
    {"8", 'H'},
    {"9", 'I'},
    {"10", 'J'},
    {"11", 'K'},
    {"12", 'M'},
    {"13", 'N'},
    {"14", 'O'},
    {"15", 'P'},
    {"16", 'Q'},
    {"17", 'R'},
    {"18", 'S'},
    {"19", 'T'},
    {"20", 'U'},
    {"21", 'V'},
    {"22", 'W'},
    {"23", 'X'},
    {"24", 'Y'},
    {"25", 'Z'}};
  vector<string> res = decodeWays(123, dict);
  for(int i = 0; i < res.size(); ++i) cout << res[i] << endl;
}


### 如何在 VSCode 中安装和配置 LeetCode 插件以及 Node.js 运行环境 #### 安装 LeetCode 插件 在 VSCode 的扩展市场中搜索 `leetcode`,找到官方提供的插件并点击 **Install** 按钮进行安装[^1]。如果已经安装过该插件,则无需重复操作。 #### 下载与安装 Node.js 由于 LeetCode 插件依赖于 Node.js 环境,因此需要下载并安装 Node.js。访问官方网站 https://nodejs.org/en/ 并选择适合当前系统的版本(推荐使用 LTS 版本)。按照向导完成安装流程后,需确认 Node.js 是否成功安装到系统环境中[^2]。 可以通过命令行运行以下代码来验证: ```bash node -v npm -v ``` 上述命令应返回对应的 Node.js 和 npm 的版本号。如果没有正常返回版本信息,则可能未正确配置环境变量。 #### 解决环境路径问题 即使完成了 Node.js 的安装,仍可能出现类似 “LeetCode extension needs Node.js installed in environment path” 或者 “command ‘leetcode.toggleLeetCodeCn’ not found” 的错误提示[^3]。这通常是因为 VSCode 未能识别全局的 Node.js 路径或者本地安装的 nvm 默认版本未被正确加载[^4]。 解决方法如下: 1. 手动指定 Node.js 可执行文件的位置 在 VSCode 设置界面中输入关键词 `leetcode`,定位至选项 **Node Path**,将其值设为实际的 Node.js 安装目录下的 `node.exe` 文件位置。例如:`C:\Program Files\nodejs\node.exe`。 2. 使用 NVM 用户管理工具调整默认版本 如果通过 nvm 工具切换了不同的 Node.js 版本,请确保设置了默认使用的版本号。可通过以下指令实现: ```bash nvm alias default <version> ``` 重新启动 VSCode 后测试功能键是否恢复正常工作状态。 --- #### 配置常用刷题语言 最后一步是在 VSCode 设置面板中的 LeetCode 插件部分定义个人习惯采用的主要编程语言作为默认提交方式之一。这样可以减少频繁修改编码风格的时间成本。 --- ### 总结 综上所述,要在 VSCode 上顺利启用 LeetCode 插件及其关联服务,除了基本插件本身外还需额外准备支持性的后台框架——即 Node.js 应用程序引擎;同时针对特定场景下产生的兼容性障碍采取针对性措施加以修正即可达成目标[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值