【动态规划】【字符串】【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

相关文章

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

一文详解如何在Python中从字符串中提取部分内容

《一文详解如何在Python中从字符串中提取部分内容》:本文主要介绍如何在Python中从字符串中提取部分内容的相关资料,包括使用正则表达式、Pyparsing库、AST(抽象语法树)、字符串操作... 目录前言解决方案方法一:使用正则表达式方法二:使用 Pyparsing方法三:使用 AST方法四:使用字

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

MySQL中动态生成SQL语句去掉所有字段的空格的操作方法

《MySQL中动态生成SQL语句去掉所有字段的空格的操作方法》在数据库管理过程中,我们常常会遇到需要对表中字段进行清洗和整理的情况,本文将详细介绍如何在MySQL中动态生成SQL语句来去掉所有字段的空... 目录在mysql中动态生成SQL语句去掉所有字段的空格准备工作原理分析动态生成SQL语句在MySQL

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

MySQL更新某个字段拼接固定字符串的实现

《MySQL更新某个字段拼接固定字符串的实现》在MySQL中,我们经常需要对数据库中的某个字段进行更新操作,本文就来介绍一下MySQL更新某个字段拼接固定字符串的实现,感兴趣的可以了解一下... 目录1. 查看字段当前值2. 更新字段拼接固定字符串3. 验证更新结果mysql更新某个字段拼接固定字符串 -