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

2024-02-07 14:44

本文主要是介绍【动态规划】【子序列除重】【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();

}

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

这篇关于【动态规划】【子序列除重】【C++算法】1987不同的好子序列数目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/688046

相关文章

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

如何用Python绘制简易动态圣诞树

《如何用Python绘制简易动态圣诞树》这篇文章主要给大家介绍了关于如何用Python绘制简易动态圣诞树,文中讲解了如何通过编写代码来实现特定的效果,包括代码的编写技巧和效果的展示,需要的朋友可以参考... 目录代码:效果:总结 代码:import randomimport timefrom math

Java中JSON字符串反序列化(动态泛型)

《Java中JSON字符串反序列化(动态泛型)》文章讨论了在定时任务中使用反射调用目标对象时处理动态参数的问题,通过将方法参数存储为JSON字符串并进行反序列化,可以实现动态调用,然而,这种方式容易导... 需求:定时任务扫描,反射调用目标对象,但是,方法的传参不是固定的。方案一:将方法参数存成jsON字

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function