【动态规划】【C++算法】639 解码方法 II

简介: 【动态规划】【C++算法】639 解码方法 II

作者推荐

视频算法专题

本文涉及知识点

动态规划汇总

字符串 滚动向量

LeetCode 639. 解码方法 II

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

‘A’ -> “1”

‘B’ -> “2”

‘Z’ -> “26”

要 解码 一条已编码的消息,所有的数字都必须分组,然后按原来的编码方案反向映射回字母(可能存在多种方式)。例如,“11106” 可以映射为:

“AAJF” 对应分组 (1 1 10 6)

“KJF” 对应分组 (11 10 6)

注意,像 (1 11 06) 这样的分组是无效的,因为 “06” 不可以映射为 ‘F’ ,因为 “6” 与 “06” 不同。

除了 上面描述的数字字母映射方案,编码消息中可能包含 '’ 字符,可以表示从 ‘1’ 到 ‘9’ 的任一数字(不包括 ‘0’)。例如,编码字符串 "1" 可以表示 “11”、“12”、“13”、“14”、“15”、“16”、“17”、“18” 或 “19” 中的任意一条消息。对 “1*” 进行解码,相当于解码该字符串可以表示的任何编码消息。

给你一个字符串 s ,由数字和 '’ 字符组成,返回 解码 该字符串的方法 数目 。
由于答案数目可能非常大,返回 109 + 7 的 模 。
示例 1:
输入:s = "
"

输出:9

解释:这一条编码消息可以表示 “1”、“2”、“3”、“4”、“5”、“6”、“7”、“8” 或 “9” 中的任意一条。

可以分别解码成字符串 “A”、“B”、“C”、“D”、“E”、“F”、“G”、“H” 和 “I” 。

因此,“" 总共有 9 种解码方法。
示例 2:
输入:s = "1

输出:18

解释:这一条编码消息可以表示 “11”、“12”、“13”、“14”、“15”、“16”、“17”、“18” 或 “19” 中的任意一条。

每种消息都可以由 2 种方法解码(例如,“11” 可以解码成 “AA” 或 “K”)。

因此,“1*” 共有 9 * 2 = 18 种解码方法。

示例 3:

输入:s = “2*”

输出:15

解释:这一条编码消息可以表示 “21”、“22”、“23”、“24”、“25”、“26”、“27”、“28” 或 “29” 中的任意一条。

“21”、“22”、“23”、“24”、“25” 和 “26” 由 2 种解码方法,但 “27”、“28” 和 “29” 仅有 1 种解码方法。

因此,“2*” 共有 (6 * 2) + (3 * 1) = 12 + 3 = 15 种解码方法。

提示:

1 <= s.length <= 105

s[i] 是 0 - 9 中的一位数字或字符 ‘*’

分析

时间复杂度:O(nm) n=s.length m = 9(1到9)。

动态规划的细节,方便检查

动态规划的状态表示 dp[j]表示s[0,i) 以 j+'A’结尾的合法串数量。dp[0]是占位符,无意义,请忽略
动态规划的转移方程 见下文
动态规划的初始状态 如果是*,dp[1,9]=1,否则dp[s[0]-‘A’]=1
动态规划的填表顺序 i从1到大,确保动态规划的无后效性
动态规划的返回值 求和 [ 1 , 26 ] j ^{j}_{[1,26]}[1,26]jdp[j]

动态规划的转移方程

有两种转移方式:

新建字符: 当前字符必须是[1,9],前一个字符不限。

和前面的串最后一个字符结合:

当前字符:[0,9] 前一字符:1

当前字符:[0,6] 前一字符 2

代码

封装类

template<int MOD = 1000000007>
class C1097Int
{
public:
  C1097Int(long long llData = 0) :m_iData(llData% MOD)
  {
  }
  C1097Int  operator+(const C1097Int& o)const
  {
    return C1097Int(((long long)m_iData + o.m_iData) % MOD);
  }
  C1097Int& operator+=(const C1097Int& o)
  {
    m_iData = ((long long)m_iData + o.m_iData) % MOD;
    return *this;
  }
  C1097Int& operator-=(const C1097Int& o)
  {
    m_iData = (m_iData + MOD - o.m_iData) % MOD;
    return *this;
  }
  C1097Int  operator-(const C1097Int& o)
  {
    return C1097Int((m_iData + MOD - o.m_iData) % MOD);
  }
  C1097Int  operator*(const C1097Int& o)const
  {
    return((long long)m_iData * o.m_iData) % MOD;
  }
  C1097Int& operator*=(const C1097Int& o)
  {
    m_iData = ((long long)m_iData * o.m_iData) % MOD;
    return *this;
  }
  bool operator<(const C1097Int& o)const
  {
    return m_iData < o.m_iData;
  }
  C1097Int pow(long long n)const
  {
    C1097Int iRet = 1, iCur = *this;
    while (n)
    {
      if (n & 1)
      {
        iRet *= iCur;
      }
      iCur *= iCur;
      n >>= 1;
    }
    return iRet;
  }
  C1097Int PowNegative1()const
  {
    return pow(MOD - 2);
  }
  int ToInt()const
  {
    return m_iData;
  }
private:
  int m_iData = 0;;
};

核心代码

class Solution {
public:
  int numDecodings(string s) {
    vector<C1097Int<>> pre(27);
    if ('*' == s[0])
    {
      for (int i = 1; i <= 9; i++)
      {
        pre[i] += 1;
      }
    }
    else
    {
      pre[s[0] - '0'] += 1;
    }
    for (int i = 1; i < s.length(); i++)
    {
      vector<C1097Int<>> dp(27);
      const auto total = std::accumulate(pre.begin()+1, pre.end(), C1097Int<>());     
      auto Add = [&dp, &pre,&total](const char& ch)
      {
        if ((ch >= '1') && (ch <= '9'))
        {
          dp[ch - '0'] += total;//新字符
        }
        dp[ch - '0' + 10] += pre[1];
        if ((ch >= '0') && (ch <= '6'))
        {
          dp[ch - '0' + 20] += pre[2];
        }
      };
      if ('*' == s[i])
      {
        for (char ch = '1'; ch <= '9'; ch++)
        {
          Add(ch);
        }
      }
      else
      {
        Add(s[i]);
      }
      pre.swap(dp);
    }
    return std::accumulate(pre.begin() + 1, pre.end(), C1097Int<>()).ToInt();
  }
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  string s;
  {
    Solution sln;
    s = "0";
    auto res = sln.numDecodings(s);
    Assert(0, res);
  }
  {
    Solution sln;
    s = "*";
    auto res = sln.numDecodings(s);
    Assert(9, res);
  }
  {
    Solution sln;
    s = "1*";
    auto res = sln.numDecodings(s);
    Assert(18, res);
  }
  {
    Solution sln;
    s = "2*";
    auto res = sln.numDecodings(s);
    Assert(15, res);
  }
  {
    Solution sln;
    s = "*********";
    auto res = sln.numDecodings(s);
    Assert(291868912, res);
  }
  
  {
    Solution sln;
    s = "**3*******";
    auto res = sln.numDecodings(s);
    Assert(351940699, res);
  }
  {
    Solution sln;
    s = "*1*3***4**6**";
    auto res = sln.numDecodings(s);
    Assert(632929394, res);
  }
  {
    Solution sln;
    s = "*1*3***4**6*7*";
    auto res = sln.numDecodings(s);
    Assert(468371306, res);
  }
}

改进

修改了三处:

一,初始化简洁。

二,total 包括dp[0]。

三,i从0开始。

代码更简洁。

class Solution {
public:
  int numDecodings(string s) {
    vector<C1097Int<>> pre(27);
    pre[0] = 1;
    for (int i = 0; i < s.length(); i++)
    {
      vector<C1097Int<>> dp(27);
      const auto total = std::accumulate(pre.begin(), pre.end(), C1097Int<>());     
      auto Add = [&dp, &pre,&total](const char& ch)
      {
        if ((ch >= '1') && (ch <= '9'))
        {
          dp[ch - '0'] += total;//新字符
        }
        dp[ch - '0' + 10] += pre[1];
        if ((ch >= '0') && (ch <= '6'))
        {
          dp[ch - '0' + 20] += pre[2];
        }
      };
      if ('*' == s[i])
      {
        for (char ch = '1'; ch <= '9'; ch++)
        {
          Add(ch);
        }
      }
      else
      {
        Add(s[i]);
      }
      pre.swap(dp);
    }
    return std::accumulate(pre.begin() + 1, pre.end(), C1097Int<>()).ToInt();
  }
};

2023年第一版

class CBigMath
 {
 public:
   static void AddAssignment(int* dst, const int& iSrc)
   {
     *dst = (*dst + iSrc) % s_iMod;
   }
   static void AddAssignment(int* dst, const int& iSrc, const int& iSrc1)
   {
     *dst = (*dst + iSrc) % s_iMod;
     *dst = (*dst + iSrc1) % s_iMod;
   }
   static void AddAssignment(int* dst, const int& iSrc, const int& iSrc1, const int& iSrc2)
   {
     *dst = (*dst + iSrc) % s_iMod;
     *dst = (*dst + iSrc1) % s_iMod;
     *dst = (*dst + iSrc2) % s_iMod;
   }
   static void SubAssignment(int* dst, const int& iSrc)
   {
     *dst = (s_iMod - iSrc + *dst) % s_iMod;
   }
   static int Add(const int& iAdd1, const int& iAdd2)
   {
     return (iAdd1 + iAdd2) % s_iMod;
   }
   static int Mul(const int& i1, const int& i2)
   {
     return((long long)i1 *i2) % s_iMod;
   }
 private:
   static const int s_iMod = 1000000007;
 };
 
  class Solution {
 public:
   int numDecodings(string s) {
     vector<int> preDp(27);
     if ('*' == s[0])
     {
       for (int i = 1; i < 10; i++)
       {
         preDp[i] = 1;
       }
     }
     else if ('0' != s[0])
     {
       preDp[s[0] - '0'] = 1;
     }
     for (int i = 1; i < s.length(); i++)
     {
       vector<int> dp(27);
       if ('*' == s[i])
       {
         for (int j = 1; j < 10; j++)
         {
           Test(dp, preDp, j);
         }
       }
       else
       {
         Test(dp, preDp, s[i] - '0');
       }
       preDp.swap(dp);
     }
     int iRet = 0;
     for (const auto& p : preDp)
     {
       CBigMath::AddAssignment(&iRet, p);
     }
     return iRet;
   }
   void Test(vector<int>& dp, const vector<int>& preDp, int iNum)
   {
     if (0 != iNum)
     {
       for (int i = 0; i < preDp.size(); i++)
       {
         CBigMath::AddAssignment(&dp[iNum], preDp[i]);
       }
     }
     CBigMath::AddAssignment(&dp[10 + iNum], preDp[1]);
     if (iNum <= 6)
     {
       CBigMath::AddAssignment(&dp[20 + iNum], preDp[2]);
     }
   }
 };

2023年1月第2版

class CBigMath

{

public:

static void AddAssignment(int* dst, const int& iSrc)

{

*dst = (dst + iSrc) % s_iMod;
}
static void AddAssignment(int
dst, const int& iSrc, const int& iSrc1)

{

*dst = (*dst + iSrc) % s_iMod;

*dst = (dst + iSrc1) % s_iMod;
}
static void AddAssignment(int
dst, const int& iSrc, const int& iSrc1, const int& iSrc2)

{

*dst = (*dst + iSrc) % s_iMod;

*dst = (*dst + iSrc1) % s_iMod;

*dst = (dst + iSrc2) % s_iMod;
}
static void SubAssignment(int
dst, const int& iSrc)

{

*dst = (s_iMod - iSrc + *dst) % s_iMod;

}

static int Add(const int& iAdd1, const int& iAdd2)

{

return (iAdd1 + iAdd2) % s_iMod;

}

static int Mul(const int& i1, const int& i2)

{

return((long long)i1 *i2) % s_iMod;

}

private:

static const int s_iMod = 1000000007;

};

class Solution {

public:

int numDecodings(string s) {

vector preDp(27);

if (‘’ == s[0])
{
for (int i = 1; i < 10; i++)
{
preDp[i] = 1;
}
}
else if (‘0’ != s[0])
{
preDp[s[0] - ‘0’] = 1;
}
for (int i = 1; i < s.length(); i++)
{
vector dp(27);
if ('
’ == s[i])

{

int iTotal = GetTotal(preDp);

for (int j = 1; j < 10; j++)

{

Test(dp, preDp, iTotal,j);

}

}

else

{

int iTotal = GetTotal(preDp);

Test(dp, preDp, iTotal,s[i] - ‘0’);

}

preDp.swap(dp);

}

return GetTotal(preDp);

}

int GetTotal(const vector& preDp)

{

int iRet = 0;

for (const auto& p : preDp)

{

CBigMath::AddAssignment(&iRet, p);

}

return iRet;

}

void Test(vector& dp, const vector& preDp,int iTotal, int iNum)

{

if (0 != iNum)

{

CBigMath::AddAssignment(&dp[iNum], iTotal);

}

CBigMath::AddAssignment(&dp[10 + iNum], preDp[1]);

if (iNum <= 6)

{

CBigMath::AddAssignment(&dp[20 + iNum], preDp[2]);

}

}

};


相关文章
|
8月前
|
API C++ Windows
Visual C++运行库、.NET Framework和DirectX运行库的作用及常见问题解决方案,涵盖MSVCP140.dll丢失、0xc000007b错误等典型故障的修复方法
本文介绍Visual C++运行库、.NET Framework和DirectX运行库的作用及常见问题解决方案,涵盖MSVCP140.dll丢失、0xc000007b错误等典型故障的修复方法,提供官方下载链接与系统修复工具使用指南。
1853 2
|
9月前
|
机器学习/深度学习 存储 算法
动态规划算法深度解析:0-1背包问题
0-1背包问题是经典的组合优化问题,目标是在给定物品重量和价值及背包容量限制下,选取物品使得总价值最大化且每个物品仅能被选一次。该问题通常采用动态规划方法解决,通过构建二维状态表dp[i][j]记录前i个物品在容量j时的最大价值,利用状态转移方程避免重复计算子问题,从而高效求解最优解。
922 1
|
9月前
|
存储 缓存 监控
用 C++ 红黑树给公司电脑监控软件的日志快速排序的方法
本文介绍基于C++红黑树算法实现公司监控电脑软件的日志高效管理,利用其自平衡特性提升日志排序、检索与动态更新效率,并结合实际场景提出优化方向,增强系统性能与稳定性。
240 4
|
8月前
|
机器学习/深度学习 数据采集 负载均衡
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
392 0
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
311 17
|
存储 机器学习/深度学习 算法
基于 C++ 的局域网访问控制列表(ACL)实现及局域网限制上网软件算法研究
本文探讨局域网限制上网软件中访问控制列表(ACL)的应用,分析其通过规则匹配管理网络资源访问的核心机制。基于C++实现ACL算法原型,展示其灵活性与安全性。文中强调ACL在企业与教育场景下的重要作用,并提出性能优化及结合机器学习等未来研究方向。
309 4
|
11月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
278 0
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
342 0
|
8月前
|
机器学习/深度学习 算法 机器人
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
736 0
|
8月前
|
数据采集 分布式计算 并行计算
mRMR算法实现特征选择-MATLAB
mRMR算法实现特征选择-MATLAB
468 2