【动态规划】【子数组划分】【前缀和】1977. 划分数字的方案数

简介: 【动态规划】【子数组划分】【前缀和】1977. 划分数字的方案数

作者推荐

【动态规划】【状态压缩】【2次选择】【广度搜索】1494. 并行课程 II

本文涉及知识点

动态规划汇总

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

LeetCode1977 划分数字的方案数

你写下了若干 正整数 ,并将它们连接成了一个字符串 num 。但是你忘记给这些数字之间加逗号了。你只记得这一列数字是 非递减 的且 没有 任何数字有前导 0 。

请你返回有多少种可能的 正整数数组 可以得到字符串 num 。由于答案可能很大,将结果对 109 + 7 取余 后返回。

示例 1:

输入:num = “327”

输出:2

解释:以下为可能的方案:

3, 27

327

示例 2:

输入:num = “094”

输出:0

解释:不能有数字有前导 0 ,且所有数字均为正数。

示例 3:

输入:num = “0”

输出:0

解释:不能有数字有前导 0 ,且所有数字均为正数。

示例 4:

输入:num = “9999999999999”

输出:101

提示:

1 <= num.length <= 3500

num 只含有数字 ‘0’ 到 ‘9’ 。

动态规划

动态规划的状态表示

dp[i][j] 表示num[0,i)组成的子序列,且最后一个元素长度为j 的数量。

动态规划的转移方程

枚举最最后一个元素的长度

dp[i+k][k] = ∑ m : 0 k − 1 \Large\sum_{m:0}^{k-1}m:0k1(pre[i][m]) 可以用前缀和优化。

如果nums(i-k,i] 小于等于nums[i,i+k) 则加上pre[i][k],可以用最长公共前缀预处理,预处理时间复杂度:O(nn)。

如果没有上面的两个优化,时间复杂度高达:O(n4)。

如果nums[i]为’0’,忽略。

动态规划的初始值

pre[0][0]=1 其它为0。

动态规划的转移方程

i从0到大。前置条件转移后置条件。

动态规划返回值

pre.back()之和。

代码

核心代码

//最长公共前缀(Longest Common Prefix)
class CLCP
{
public:
  CLCP(const string& str1, const string& str2)
  {
    m_dp.assign(str1.length() , vector<int>(str2.length()));
    //str1[j...)和str2[k...]比较时, j和k不断自增,总有一个先到达末端
    for (int i = 0; i < str1.length(); i++)
    {//枚举str2 先到末端 str1[i]和str2.back对应
      m_dp[i][str2.length() - 1] = (str1[i] == str2.back());
      for (int j = i-1 ; j >= 0 ; j-- )
      {
        const int k = str2.length() - 1 - (i-j);
        if (k < 0)
        {
          break;
        }
        if (str1[j] == str2[k])
        {
          m_dp[j][k] = 1 + m_dp[j + 1][k + 1];
        }
      }     
    }
    for (int i = 0; i < str2.length(); i++)
    {//枚举str1 先到末端 str2[i]和str1.back对应
      m_dp[str1.length()-1][i] = (str1.back() == str2[i]);
      for (int j = i - 1; j >= 0; j--)
      {
        const int k = str1.length() - 1 - (i-j);
        if (k < 0)
        {
          break;
        }
        if (str1[k] == str2[j])
        {
          m_dp[k][j] = 1 + m_dp[k + 1][j + 1];
        }
      }
    }
  }
  vector<vector<int>> m_dp;
};
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 numberOfCombinations(string num) {
    m_c = num.length();
    CLCP lcp(num, num);
    vector<vector<C1097Int<> >> dp(m_c + 1, vector<C1097Int<> >(m_c + 1));
    dp[0][0] = 1;
    for (int i = 0; i < m_c; i++)
    {
      if ('0' == num[i])
      {
        continue;
      }
      C1097Int biSum = 0;
      for (int j = 1; i + j <= m_c; j++)
      {
        biSum += dp[i][j - 1];
        dp[i + j][j] += biSum;
        bool bMoreEqual = false;
        if ((i >= j))
        {
          const int len = lcp.m_dp[i][i - j];
          if (len  >= j)
          {
            bMoreEqual = true;
          }
          else
          {
            bMoreEqual = num[i + len] > num[i - j + len];
          }
        }
        if (bMoreEqual)
        {
          dp[i + j][j] += dp[i][j];
        }
      }
    }
    return std::accumulate(dp.back().begin(), dp.back().end(), C1097Int()).ToInt();
  }
  int m_c;
};

测试用例

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 num;
  {
    Solution sln;
    num = "327";
    auto res = sln.numberOfCombinations(num);
    Assert(res,2);
  }
  {
    Solution sln;
    num = "094";
    auto res = sln.numberOfCombinations(num);
    Assert(res, 0);
  }
  {
    Solution sln;
    num = "0";
    auto res = sln.numberOfCombinations(num);
    Assert(res, 0);
  }
  {
    Solution sln;
    num = "9999999999999";
    auto res = sln.numberOfCombinations(num);
    Assert(res, 101);
  }
}

2023年2月

class C1097Int

{

public:

C1097Int(int iData = 0) :m_iData(iData)

{

}

C1097Int operator+(const C1097Int& o)const

{

return C1097Int(((long long)m_iData + o.m_iData) % s_iMod);

}

C1097Int& operator+=(const C1097Int& o)

{

m_iData = ((long long)m_iData + o.m_iData) % s_iMod;

return this;
}
C1097Int operator
(const C1097Int& o)const

{

return((long long)m_iData o.m_iData) % s_iMod;
}
C1097Int& operator
=(const C1097Int& o)

{

m_iData =((long long)m_iData *o.m_iData) % s_iMod;

return *this;

}

bool operator<(const C1097Int& o)const

{

return m_iData < o.m_iData;

}

C1097Int& pow( int n)const

{

C1097Int iRet = 1, iCur = *this;

while (n)

{

if (n & 1)

{

iRet *= iCur;

}

iCur *= iCur;

n >>= 1;

}

return iRet;

}

C1097Int PowNegative1()

{

return pow(s_iMod - 2);

}

int ToInt()const

{

return m_iData;

}

private:

int m_iData = 0;;

static const int s_iMod = 1000000007;

};

int operator+(int iData, const C1097Int& int1097)

{

int iRet = int1097.operator+(C1097Int(iData)).ToInt();

return iRet;

}

int& operator+=(int& iData, const C1097Int& int1097)

{

iData = int1097.operator+(C1097Int(iData)).ToInt();

return iData;

}

int operator*(int iData, const C1097Int& int1097)

{

int iRet = int1097.operator*(C1097Int(iData)).ToInt();

return iRet;

}

int& operator*=(int& iData, const C1097Int& int1097)

{

iData = int1097.operator*(C1097Int(iData)).ToInt();

return iData;

}

template

void MinSelf(T* seft, const T& other)

{

*seft = min(*seft, other);

}

template

void MaxSelf(T* seft, const T& other)

{

*seft = max(*seft, other);

}

int GetNotRepeateNum(int len, int iHasSel)

{

if (0 == len)

{

return 1;

}

if ((0 == iHasSel) && (1 == len))

{

return 10;

}

int iRet = 1;

if (iHasSel > 0)

{

for (int tmp = 10 - iHasSel; (tmp >= 2)&& len ; tmp–,len–)

{

iRet *= tmp;

}

}

else

{

iRet *= 9;

len–;

for (int tmp=9; (tmp>=2)&&len; len–,tmp–)

{

iRet *= tmp;

}

}

return iRet;

}

int GCD(int n1, int n2)

{

int t1 = min(n1, n2);

int t2 = max(n1, n2);

if (0 == t1)

{

return t2;

}

return GCD(t2%t1, t1);

}

void CreateMaskVector(vector& v,const int* const p,int n )

{

const int iMaxMaskNum = 1 << n;

v.resize(iMaxMaskNum);

for (int i = 0; i < n; i++)

{

v[1 << i] = p[i];

}

for (int mask = 1; mask < iMaxMaskNum ; mask++)

{

const int iSubMask = mask&(-mask);

v[mask] = v[iSubMask] + v[mask-iSubMask];

}

}

class Solution {

public:

int numberOfCombinations(string num) {

m_c = num.size();

if (‘0’ == num.begin())
{
return 0;
}
m_vDpCmpStr.assign(m_c, vector(m_c));
for (int i = m_c-1 ; i >=0 ; i–)
{
for (int j = m_c - 1; j >= 0; j-- )
{
if (num[i] != num[j])
{
continue;
}
m_vDpCmpStr[i][j] = 1;
if ((i + 1 < m_c) && (j + 1 < m_c))
{
m_vDpCmpStr[i][j] += m_vDpCmpStr[i + 1][j + 1];
}
}
}
vector<vector> dp(m_c, vector(m_c));
dp[0].assign(m_c, 1);
for (int iPos = 1; iPos < m_c; iPos++)
{
if ((‘0’ == num[iPos]) /
&& (1 != len)*/)

{

continue;

}

int preLen = 1;

C1097Int sum(0);

for (int end = iPos; end < m_c; end++)

{

const int len = end - iPos + 1;

for (int i = 0; i < 2; i++)

{

const int iPrePos = iPos - preLen;

if (iPrePos >= 0)

{

const int iPubLen = m_vDpCmpStr[iPrePos][iPos];

bool Less = (preLen == len) && ((iPubLen >= len) || (num[iPrePos + iPubLen] < num[iPos + iPubLen] ));

if ((preLen < len) || Less )

{

preLen++;

sum += dp[iPrePos][iPos - 1];

}

}

}

dp[iPos][end] += sum;

}

}

C1097Int intSum;

for (const auto& d : dp)

{

intSum += d.back();

}

return intSum.ToInt();

}

int m_c;

vector<vector> m_vDpCmpStr;

};


相关文章
|
6月前
|
算法 测试技术 C#
【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目
【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目
|
6月前
|
算法 测试技术 C++
【动态规划】【前缀和】【数学】2338. 统计理想数组的数目
【动态规划】【前缀和】【数学】2338. 统计理想数组的数目
|
6月前
|
算法 测试技术 C++
【动态规划】【离线查询】【前缀和】689. 三个无重叠子数组的最大和
【动态规划】【离线查询】【前缀和】689. 三个无重叠子数组的最大和
|
6月前
DAY-4 | 力扣 - 求自身以外数组的乘积:区间划分,左右累乘,巧求乘积
该文档是关于LeetCode上的一道题目“Product of Array Except Self”的题解。提供了两种解题方法,一是暴力破解,即计算所有数的乘积后再逐个除以当前元素;二是左右累乘法,通过两次遍历数组分别计算左侧和右侧元素的乘积,避免了除法操作。其中,左右累乘法更优,代码实现中展示了这种方法。
43 1
|
6月前
|
算法 测试技术 C++
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
|
6月前
|
算法 测试技术 C#
【线段树 区间位运算模板】3117划分数组得到最小的值之和
【线段树 区间位运算模板】3117划分数组得到最小的值之和
|
6月前
|
算法 测试技术 C++
【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数
【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数
|
6月前
[leetcode 数位运算] 2578.最小分割和
[leetcode 数位运算] 2578.最小分割和
|
6月前
代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
51 0
|
6月前
|
Java
leetcode-763:划分字母区间
leetcode-763:划分字母区间
45 0