剑指 Offer(第2版)面试题 46:把数字翻译成字符串

剑指 Offer(第2版)面试题 46:把数字翻译成字符串

题目来源:59. 把数字翻译成字符串

解法1:动态规划

还记得经典的爬楼梯(斐波那契数列)吗?

每次可以走 1 步或者 2 步,问 n 个台阶一共有多少种爬楼梯的方法?

我们不需要思考就能写出状态转移方程:dp[i] = dp[i-1] + dp[i-2]。

这道题相当于加了一些限制条件,并且倒着推更便于理解。

设 dp[i] 表示从字符串的第 i 位开始到末尾,最大的翻译方法个数。

初始化 dp[n] = 1,dp[n - 1] = 1。

状态转移:

  1. dp[i] = dp[i + 1],先假设 s[i] 只有一种翻译方法,此时翻译方法保持不变。
  2. 如果 s[i] 和 s[i+1] 能够翻译成一个字母,则 dp[i] 的翻译方法可以加上 dp[i+2] 的值。

举例:

假设数字为 12xxxxxx,此时考虑到 1,有 2 种情况:

  1. 1 作为单独的一个数看,翻译方法数和 2xxxxxx 相同.
  2. 12 作为一个整体看,翻译方法数和 xxxxxx 相同。

代码:

class Solution
{
public:
	int getTranslationCount(string s)
	{
		if (s.empty())
			return 0;
		int n = s.size();
		if (n == 1)
			return 1;
		vector<int> dp(n + 1, 0);
		// 初始化
		dp[n] = dp[n - 1] = 1;
		// 状态转移
		for (int i = n - 2; i >= 0; i--)
		{
			dp[i] = dp[i + 1];
			if (s[i] == '1' || (s[i] == '2' && s[i + 1] >= '0' && s[i + 1] <= '5'))
				dp[i] += dp[i + 2];
		}
		return dp[0];
	}
};

复杂度分析:

时间复杂度:O(n),其中 n 是字符串 s 的长度。

空间复杂度:O(n),其中 n 是字符串 s 的长度。

我们发现 dp[i] 的值只与 dp[i+1] 和 dp[i+2] 有关,可以状态压缩:

class Solution
{
public:
	int getTranslationCount(string s)
	{
		if (s.empty())
			return 0;
		int n = s.size();
		if (n == 1)
			return 1;
		// vector<int> dp(n + 1, 0);
		// 初始化
		// dp[n] = dp[n - 1] = 1;
		int cur = 0, pre1 = 1, pre2 = 1;
		// 状态转移
		for (int i = n - 2; i >= 0; i--)
		{
			// dp[i] = dp[i + 1];
			cur = pre1;
			if (s[i] == '1' || (s[i] == '2' && s[i + 1] >= '0' && s[i + 1] <= '5'))
			{
			    // dp[i] += dp[i + 2];
			    cur += pre2;
			}
			pre2 = pre1;
			pre1 = cur;
		}
		return cur;
	}
};

还可以顺着进行动态规划:

class Solution
{
public:
    int getTranslationCount(string s)
    {
        if (s.empty())
			return 0;
		int n = s.size();
		if (n == 1)
			return 1;
		vector<int> dp(n + 1, 0);
		// 初始化
		dp[0] = dp[1] = 1;
		// 状态转移
		for (int i = 2; i <= n; i++)
		{
		    dp[i] = dp[i - 1];
		    if (s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] >= '0' && s[i - 1] <= '5'))
		        dp[i] += dp[i - 2];
		}
		return dp[n];
    }
};

类似题:LeetCode 91. 解码方法

链接:91. 解码方法

从 1 编码到 26,这里 0 是非法字符!

代码:

// 顺推动态规划

class Solution
{
public:
    int numDecodings(string s)
    {
        // 特判(s 只包含数字,并且可能包含前导零)
        if (s.empty() || s[0] == '0')
            return 0;
        if (s.size() == 1)
            return 1;
        int n = s.size(), pre = s[0] - '0';
        // 状态矩阵,并初始化(每个数字均可映射成一个字母)
        vector<int> dp(n + 1, 1);
        // 状态转移
        for (int i = 2; i <= n; i++)
        {
            int cur = s[i - 1] - '0';
            if (cur == 0 && (pre != 1 && pre != 2))
                return 0;
            if (pre == 1 || (pre == 2 && cur <= 6)) // 当前数字为 10~26
            {
                if (cur != 0) // cur 不为 0,则当前数字可以拆分,方法数累加
                    dp[i] = dp[i - 2] + dp[i - 1];
                else // 当前数字为 10或 20,不可拆分,只有一种映射,解码方法不增加
                    dp[i] = dp[i - 2];
            }
            else // 当前数字为1~9,只有一种映射,解码方法不增加
                dp[i] = dp[i - 1];
            pre = cur;
        }
        return dp[n];
    }
};

倒推会出现问题,这道题并不保证任何数字都可以翻译成字符串。

简化:

// 简化

class Solution
{
public:
    int numDecodings(string s)
    {
        // 特判(s 只包含数字,并且可能包含前导零)
        if (s.empty() || s[0] == '0')
            return 0;
        if (s.size() == 1)
            return 1;
        int n = s.size();
        s = " " + s;
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            int a = s[i] - '0', b = (s[i - 1] - '0') * 10 + s[i] - '0';
            if (a >= 1 && a <= 9)
                dp[i] = dp[i - 1];
            if (b >= 10 && b <= 26)
                dp[i] += dp[i - 2];
        }
        return dp[n];
    }
};

复杂度分析:

时间复杂度:O(n),其中 n 是字符串 s 的长度。

空间复杂度:O(n),其中 n 是字符串 s 的长度。

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值