【动态规划】【字符串】【C++算法】940. 不同的子序列 II

2024-01-24 08:28

本文主要是介绍【动态规划】【字符串】【C++算法】940. 不同的子序列 II,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者推荐

【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径

本文涉及知识点

动态规划汇总

LeetCode940. 不同的子序列 II

给定一个字符串 s,计算 s 的 不同非空子序列 的个数。因为结果可能很大,所以返回答案需要对 10^9 + 7 取余 。
字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。
例如,“ace” 是 “abcde” 的一个子序列,但 “aec” 不是。
示例 1:
输入:s = “abc”
输出:7
解释:7 个不同的子序列分别是 “a”, “b”, “c”, “ab”, “ac”, “bc”, 以及 “abc”。
示例 2:
输入:s = “aba”
输出:6
解释:6 个不同的子序列分别是 “a”, “b”, “ab”, “ba”, “aa” 以及 “aba”。
示例 3:
输入:s = “aaa”
输出:3
解释:3 个不同的子序列分别是 “a”, “aa” 以及 “aaa”。
参数范围
1 <= s.length <= 2000
s 仅由小写英文字母组成

动态规划

动态规划的状态表示

pre[j]表示前i个字符,以’a’+j 结尾的字符数量。dp[j]表示前i+1个字符,以’a’+j 结尾的字符数量。

动态规划的转移方程

{ 处理 i = 0 26 d p [ j ] + = p r e [ j ] 不选择 s [ i ] ,情况一 d p [ s [ i ] − ′ a ′ ] + = ∑ i = 0 26 p r e [ i ] + 1 , 选择 s [ i ] ,情况二 d p [ s [ i ] − ′ a ′ ] − = p r e [ s [ i ] − ′ a ′ ] 去掉重复 \begin{cases} 处理 \Large^{26}_{i=0} dp[j] += pre[j] & 不选择s[i] ,情况一\\ dp[s[i]-'a']+= \sum\Large_{i=0}^{26}pre[i] +1, & 选择s[i],情况二 \\ dp[s[i]-'a'] -= pre[s[i]-'a'] & 去掉重复 \end{cases} 处理i=026dp[j]+=pre[j]dp[s[i]a]+=i=026pre[i]+1,dp[s[i]a]=pre[s[i]a]不选择s[i],情况一选择s[i],情况二去掉重复
情况一和情况二内部不会重复。结束字符不同不会重复,故只需要考虑结束字符相同。
任意 pre[s[i]-‘a’] 去掉最后一个字符换成s[i],都是合法的情况二。 → \rightarrow 结束字符相同的情况一,全部重复,排除。
选择的情况不能直接2i,否则会有重复。 那个1表示空串。

动态规划的填表顺序

i从1到大

动态规划的初始值

pre[s[0]-‘a’]=1,其它为0。

动态规划的返回值

∑ i = 0 26 \sum\Large_{i=0}^{26} i=026pre[i]

代码

核心代码

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 distinctSubseqII(string s) {vector<C1097Int<>> pre(26);pre[s.front() - 'a'] = 1;for (int i = 1; i < s.length(); i++){vector<C1097Int<>> dp(26);C1097Int<> total = std::accumulate(pre.begin(), pre.end(), C1097Int<>(1));for (int j = 0; j < 26; j++){if ('a' + j != s[i]){dp[j] += pre[j];}else{dp[j] += total;}}pre.swap(dp);}return std::accumulate(pre.begin(), 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 = "abc";auto res = sln.distinctSubseqII(s);Assert(res, 7);}{Solution sln;s = "aba";auto res = sln.distinctSubseqII(s);Assert(res, 6);}{Solution sln;s = "aaa";auto res = sln.distinctSubseqII(s);Assert(res, 3);}{Solution sln;s = "adddddddddddddddddddddddddd";auto res = sln.distinctSubseqII(s);Assert(res, 53);}{Solution sln;s = "ddddddddcdddddddfdddddddddedddddddddddddddd";auto res = sln.distinctSubseqII(s);Assert(res, 20611);}{Solution sln;s = "abcdefghijklmnopqrstuvwxyzzzzaaa";auto res = sln.distinctSubseqII(s);Assert(res, 671088636);}}

2023年1月

class C1097Int
{
public:
C1097Int(int iData = 0) :m_iData(iData)
{

 }C1097Int  operator+(const C1097Int& o)const{return C1097Int((m_iData + o.m_iData) % s_iMod);}C1097Int&  operator+=(const C1097Int& o){m_iData = (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;}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;
}

class Solution {
public:
int distinctSubseqII(string s) {
m_resutl.resize(26);
for (int i = 0; i < 26; i++)
{
m_resutl[i].assign(s.length() + 1, -1);
}
C1097Int ret = 0;
for (char ch = ‘a’; ch <= ‘z’; ch++)
{
ret += Rev(0, s, ch);
}
return ret.ToInt();
}
C1097Int Rev(int iBegin, const string& s,const char beginChar)
{
int& iResult = m_resutl[beginChar - ‘a’][iBegin];
if (-1 != iResult)
{
return iResult;
}
for (; (iBegin < s.length()) && (beginChar != s[iBegin]); iBegin++);
if (s.length() == iBegin)
{
return iResult=0;
}
C1097Int ret =1 ;
for (char ch = ‘a’; ch <= ‘z’; ch++)
{
ret += Rev(iBegin + 1, s, ch);
}
return iResult = ret.ToInt();
}
vector<vector> m_resutl;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++算法】940. 不同的子序列 II的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

使用Python实现base64字符串与图片互转的详细步骤

《使用Python实现base64字符串与图片互转的详细步骤》要将一个Base64编码的字符串转换为图片文件并保存下来,可以使用Python的base64模块来实现,这一过程包括解码Base64字符串... 目录1. 图片编码为 Base64 字符串2. Base64 字符串解码为图片文件3. 示例使用注意

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可