【动态规划】【子序列除重】【C++算法】1987不同的好子序列数目

简介: 【动态规划】【子序列除重】【C++算法】1987不同的好子序列数目

作者推荐

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

本文涉及知识点

动态规划汇总

LeetCode1987:不同的好子序列数目

给你一个二进制字符串 binary 。 binary 的一个 子序列 如果是 非空 的且没有 前导 0 (除非数字是 “0” 本身),那么它就是一个 好 的子序列。

请你找到 binary 不同好子序列 的数目。

比方说,如果 binary = “001” ,那么所有 好 子序列为 [“0”, “0”, “1”] ,所以 不同 的好子序列为 “0” 和 “1” 。 注意,子序列 “00” ,“01” 和 “001” 不是好的,因为它们有前导 0 。

请你返回 binary 中 不同好子序列 的数目。由于答案可能很大,请将它对 109 + 7 取余 后返回。

一个 子序列 指的是从原数组中删除若干个(可以一个也不删除)元素后,不改变剩余元素顺序得到的序列。

示例 1:

输入:binary = “001”

输出:2

解释:好的二进制子序列为 [“0”, “0”, “1”] 。

不同的好子序列为 “0” 和 “1” 。

示例 2:

输入:binary = “11”

输出:2

解释:好的二进制子序列为 [“1”, “1”, “11”] 。

不同的好子序列为 “1” 和 “11” 。

示例 3:

输入:binary = “101”

输出:5

解释:好的二进制子序列为 [“1”, “0”, “1”, “10”, “11”, “101”] 。

不同的好子序列为 “0” ,“1” ,“10” ,“11” 和 “101” 。

提示:

1 <= binary.length <= 105

binary 只含有 ‘0’ 和 ‘1’ 。

动态规划

除0外,不存在以0开始的子序列。如果存在0,则必定存在子序列{0}。以下的分析排除{0}。

排除{0}后任意合法子序列在后面增加0或1,都是合法子序列。

动态规划的状态表示

pre[0] 从binary[0,i)中选择若干字符,形成以0结束的合法子序列数量。pre[1]以1结束的子序列数量。

dp和pre类似,对应的是binary[0,i+1)。

动态规划的转移方程

binary[i]为1

{ p r e [ 0 ] 不选择当前字符,以 0 结束的字符数量 情况一 p r e [ 1 ] 不选择当前字符,以 1 结束的字符数 情况二 p r e [ 0 ] + p r e [ 1 ] + 1 选择当前字符,以 1 结束的字符数量。 情况三 \begin{cases} pre[0] & 不选择当前字符,以0结束的字符数量 & 情况一 \\ pre[1] & 不选择当前字符,以1结束的字符数 & 情况二 \\ pre[0]+pre[1]+1 & 选择当前字符,以1结束的字符数量。 & 情况三 \\ \end{cases}pre[0]pre[1]pre[0]+pre[1]+1不选择当前字符,以0结束的字符数量不选择当前字符,以1结束的字符数选择当前字符,以1结束的字符数量。情况一情况二情况三

情况三又可以分三种情况:

{ p r e [ 0 ] 倒数第二个字符是 0 情况三一 p r e [ 1 ] 倒数第二个字符是 1 情况三二 1 子序列 1 。 情况三三 \begin{cases} pre[0] & 倒数第二个字符是0 & 情况三一 \\ pre[1] & 倒数第二个字符是1 & 情况三二 \\ 1 & 子序列{1}。 & 情况三三 \\ \end{cases}pre[0]pre[1]1倒数第二个字符是0倒数第二个字符是1子序列1情况三一情况三二情况三三

情况一、情况二、情况三 内部不存在重复情况。

情况一以0结尾,情况二、三以1结尾,所以情况一和情况二(三)不会重复。

情况二所有的情况都和情况三重合,情况二分类:

{ 倒数第二个字符是 0 被情况三一包含 倒数第二个字符是 1 被情况三二包含 子序列 1 。 和情况三三重复 \begin{cases} 倒数第二个字符是0 & 被情况三一包含 \\ 倒数第二个字符是1 & 被情况三二包含 \\ 子序列{1}。 & 和情况三三 重复\\ \end{cases}倒数第二个字符是0倒数第二个字符是1子序列1被情况三一包含被情况三二包含和情况三三重复

总结

dp[1] = pre[0]+pre[1]+1

dp[0] = pre[0]

binary[i]为0

不能为子序列{0}

dp[0] = pre[0]+pre[1]

dp[1] = pre[1]

动态规划的初始值

pre 全为0。

动态规划的返回值

pre之和。

代码

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 numberOfUniqueGoodSubsequences(string binary) {
    vector<C1097Int<>> pre(2);
    for (const auto& ch : binary)
    {
      pre = {('0'==ch)? (pre[0] + pre[1]):pre[0],('1' == ch) ? (pre[0] + pre[1]+1) : pre[1] };
    }
    int iZero = std::count(binary.begin(), binary.end(), '0') > 0;
    return (pre[0] + pre[1] + iZero).ToInt();
  }
};

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;

}

class Solution {

public:

int numberOfUniqueGoodSubsequences(string binary) {

vector pre(2);

for (const auto& ch : binary)

{

vector dp(2);

if (‘0’ == ch)

{

pre[0] += pre[1];

}

else

{

pre[1] += pre[0];

pre[1] += 1;

}

}

return (pre[0] + pre[1] + (int)(-1 != binary.find(‘0’))).ToInt();

}

};

2023年7月

class Solution {

public:

int numberOfUniqueGoodSubsequences(string binary) {

bool bHasZero = binary[0] == ‘0’;

vector<C1097Int<>> pre(2);

pre[1] = (binary[0] == ‘1’);

for (int i = 1; i < binary.size(); i++)

{

vector<C1097Int<>> dp = pre ;

if (‘0’ == binary[i])

{

bHasZero = true;

dp[0] = pre[0] + pre[1];

}

else

{

dp[1] = pre[0] + pre[1] + 1;

}

pre.swap(dp);

}

return (C1097Int<>(bHasZero) + pre[0] + pre[1]).ToInt();

}

};


相关文章
|
1月前
|
算法 测试技术 C++
【动态规划算法】蓝桥杯填充问题(C/C++)
【动态规划算法】蓝桥杯填充问题(C/C++)
|
9天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
26 2
|
1月前
|
算法
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
65 2
动态规划算法学习三:0-1背包问题
|
1月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
65 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
1月前
|
算法
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
118 0
动态规划算法学习二:最长公共子序列
|
1月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
101 0
|
1月前
|
算法 C++
【算法解题思想】动态规划+深度优先搜索(C/C++)
【算法解题思想】动态规划+深度优先搜索(C/C++)
|
25天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
10天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
11天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。