C++前缀和算法的应用:DI序列的有效排列的原理、源码及测试用例

news2024/12/23 5:18:03

本文涉及的基础知识点

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

题目

给定一个长度为 n 的字符串 s ,其中 s[i] 是:
“D” 意味着减少,或者
“I” 意味着增加
有效排列 是对有 n + 1 个在 [0, n] 范围内的整数的一个排列 perm ,使得对所有的 i:
如果 s[i] == ‘D’,那么 perm[i] > perm[i+1],以及;
如果 s[i] == ‘I’,那么 perm[i] < perm[i+1]。
返回 有效排列 perm的数量 。因为答案可能很大,所以请返回你的答案对 109 + 7 取余。
示例 1:
输入:s = “DID”
输出:5
解释:
(0, 1, 2, 3) 的五个有效排列是:
(1, 0, 3, 2)
(2, 0, 3, 1)
(2, 1, 3, 0)
(3, 0, 2, 1)
(3, 1, 2, 0)
示例 2:
输入: s = “D”
输出: 1

分析

用动态规划解决,时间复杂度O(nnn)。用strRes表示结果串,用n表示它的长度,每个数字的取值范围[0,n)。dp[i][j],表示前i+1个数已经处理,未处理的数中共有j个数大于strRes[i]。状态数共有n*n个,状态转换的时间复杂是O(n)。以上升为例:只需要考虑j个比strRes[i]数,选择最小的数,则jNew=j-1;选择次小的数, jNew=j-2…。下降类似,小于strRes[i]的数,可以计算出来:总数-已经处理的数-未处理的数中大于res[i]=n-(i+1)-j。注意:各位数各不相等,所以已经处理的数(如:res[i])是不会在出现的,所以未处理的数,需要考虑大于和小于,不需要考虑等于。

代码

template
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;;
};

template
int operator+(int iData, const C1097Int& int1097)
{
int iRet = int1097.operator+(C1097Int(iData)).ToInt();
return iRet;
}

template
int& operator+=(int& iData, const C1097Int& int1097)
{
iData = int1097.operator+(C1097Int(iData)).ToInt();
return iData;
}

template
int operator*(int iData, const C1097Int& int1097)
{
int iRet = int1097.operator*(C1097Int<>(iData)).ToInt();
return iRet;
}

template
int& operator*=(int& iData, const C1097Int& int1097)
{
iData = int1097.operator*(C1097Int<>(iData)).ToInt();
return iData;
}

class Solution {
public:
	int numPermsDISequence(string s) {
		//n:结果串的长度 每个数字的取值范围[0,n)
		int n = s.length() + 1;
		vector<C1097Int<>> pre(n);//记录有多少个数大于当前数
		for (int i = 0; i < n; i++)
		{
			pre[n - i -1] += 1;
		}
		for (int i = 0; i < s.length(); i++)
		{
			vector<C1097Int<>> dp(n );
			if ('I' == s[i])
			{
				for (int iMore = 1; iMore < n; iMore++)
				{
					for (int j = 0; j < iMore; j++)
					{
						dp[iMore - j - 1] += pre[iMore];
					}
				}
			}
			else
			{
				for (int iMore = 0; iMore < n; iMore++)
				{
					const int less = n - (i + 1) - iMore;
					if (less < 1)
					{
						break;
					}
					for (int j = 0; j < less; j++)
					{
						const int iNewLess = less - j - 1;
						const int iNewMore = n - (i + 1 + 1) - iNewLess;
						dp[iNewMore] += pre[iMore];
					}
				}
			}
			pre.swap(dp);
		}
		auto bi = std::accumulate(pre.begin(), pre.end(), C1097Int<>(0));
		return bi.ToInt();
	}
};

测试用例

int main()
{
int res = Solution().numPermsDISequence(“”);
Assert(1, res);
res = Solution().numPermsDISequence(“I”);
Assert(1, res);
res = Solution().numPermsDISequence(“D”);
Assert(1, res);
res = Solution().numPermsDISequence(“II”);
Assert(1, res);
res = Solution().numPermsDISequence(“ID”);
Assert(2, res);
res = Solution().numPermsDISequence(“DD”);
Assert(1, res);
res = Solution().numPermsDISequence(“DI”);
Assert(2, res);
res = Solution().numPermsDISequence(“DID”);
Assert(5, res);
res = Solution().numPermsDISequence(“IDD”);
Assert(3, res);
res = Solution().numPermsDISequence(“DDI”);
Assert(3, res);
res = Solution().numPermsDISequence(“DII”);
Assert(3, res);
res = Solution().numPermsDISequence(“IDI”);
Assert(5, res);
res = Solution().numPermsDISequence(“IID”);
Assert(3, res);
//CConsole::Out(res);
}

优化

可以利用前缀和,将时间复杂度降到O(n*n)。

iMore取[1,…]中任意数,选择最大的数,则jNew都等于0
iMore取[2,…]中任意数,选择次大的数,则jNew都等于1
iMore取[3,…] 中任意数,选择第三大的数,则jNew都等于2
结论:
dp[0] = sum(pre[1]…pre[n-1])
dp[1] = sum(pre[2]…pre[n-1])
dp[2] = sum(pre[3]…pre[n-1])

代码

class Solution {
public:
	int numPermsDISequence(string s) {
		//n:结果串的长度 每个数字的取值范围[0,n)
		int n = s.length() + 1;
		vector<C1097Int<>> pre(n);//记录有多少个数大于当前数
		for (int i = 0; i < n; i++)
		{
			pre[n - i -1] += 1;
		}
		for (int i = 0; i < s.length(); i++)
		{
			vector<C1097Int<>> dp(n );
			if ('I' == s[i])
			{
				C1097Int<> biSum = 0;
				for (int j = n-1; j > 0 ; j-- )
				{
					biSum += pre[j];
					dp[j-1] = biSum;
				}
			}
			else
			{
				C1097Int<> biSum = 0;
				for (int j = n - 1; j > 0; j--)
				{//j是小于strRes[i]的数目
					const int iMore = n - (i + 1) - j;
					if (iMore < 0)
					{
						continue;
					}
					const int iNewMore = n - (i + 1+1) - (j-1);
					biSum += pre[iMore];
					dp[iNewMore] = biSum;
				}
			}
			pre.swap(dp);
		}
		auto bi = std::accumulate(pre.begin(), pre.end(), C1097Int<>(0));
		return bi.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1113801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

短视频矩阵系统源码(搭建)

短视频矩阵源码的开发路径分享如下&#xff1a; 1、首先&#xff0c;确定项目需求和功能&#xff0c;包括用户上传、编辑、播放等。 2、其次&#xff0c;搭建开发环境&#xff0c;选择合适的开发工具和框架。 3、然后&#xff0c;进行项目架构设计和数据库设计&#xff0c;确…

正点原子嵌入式linux驱动开发——Linux内核定时器

定时器是最常用到的功能&#xff0c;一般用来完成定时功能&#xff0c;本章就来学习一下Linux内核提供的定时器API函数&#xff0c;通过这些定时器API函数可以完成很多要求定时的应用。 Linux内核也提供了短延时函数&#xff0c;比如微秒、纳秒、毫秒延时函数&#xff0c;本章就…

shell命令行参数

#!/bin/bash echo "Number of arguments: $#"echo "All arguments as a single string: $*"echo "All arguments as a single string: $0,$1,$2" for i in "$" doecho "这是第 $i 次循环" done

拭目以待!万博智云亮相2023长沙·中国1024程序员节

2023年10月23 - 25日&#xff0c;第四届“长沙中国 1024 程序员节”将在中国长沙盛大举行&#xff0c;万博智云 OneProCloud将以大会钻石合作伙伴身份重磅亮相&#xff0c;CEO Michael将发表主题演讲。这也是万博智云首次成为长沙中国 1024 程序员节合作伙伴。 1.主题演讲 万博…

设备健康管理系统PreMaint如何帮助制药企业符合GMP认证要求

在制药行业&#xff0c;确保药品的质量、安全性和有效性是至关重要的。为了满足这一需求&#xff0c;药品生产质量管理规范&#xff08;GMP&#xff09;被广泛采用作为制药企业达到国际质量标准的基础。然而&#xff0c;制药企业在追求GMP认证标准时面临着不少挑战。本文将探讨…

高数二阶导数例子

例子&#xff1a; 当 x 0 x 0 x0时 f(x) 1&#xff0c;当 x ≠ 0 x \ne 0 x0时&#xff0c; f ( x ) sin ⁡ x x f(x) \frac{\sin x}{x} f(x)xsinx​, 求f’(x)。 解&#xff1a; 二阶导数是导数的导数&#xff0c;按照定义为&#xff1a; f ′ ′ ( x ) lim ⁡ x →…

【Java基础】JDK下载安装及环境配置完整教程

文章目录 1.jdk的下载2.安装jdk3.配置jdk的环境变量4.验证jdk配置环境变量是否配置成功5.配置环境变量的作用JAVA_HOMEPathCLASSPATH 1.jdk的下载 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/#java8-windows 注&#xff1a;此处下载需要oracle账…

带声学释放器的近海海底潜标的回收记录

我们主要在大洋调查中使用带声学释放器的海底潜标&#xff0c;在近岸海域很少这样做&#xff0c;因为近岸海域拖网作业较多&#xff0c;海底潜标很容易被渔网拖走或移位。前段时间&#xff0c;我们在近海也使用了这种方式&#xff0c;主要考虑到测区水深较深&#xff0c;即使是…

分析并实现Android中的MVC、MVP架构模式

架构是什么 架构是为了解决特定的问题而提出来的&#xff0c;而且它还有特定的规则&#xff0c;能够把整个应用的整体进行角色的划分。并且他还能够约定角色之间的联系沟通机制。 所以学习架构要带着以下三个问题去理解&#xff1a; 。架构解决了什么问题&#xff1f; 。架…

MobaXtem通过SSH远程登录ubuntu系统

工具&#xff1a;一个装windows10的笔记本电脑工控机路由器&#xff0c;工控机中安装的ubuntu16&#xff0c;通过网线和路由器相连。电脑连接路由器的wifi&#xff0c;就可以和工控机处于同一个局域网环境中&#xff0c;记得要修改电脑和工控机在同一个网段。 一、在ubuntu上安…

麒麟系统开发笔记(十三):在国产麒麟系统上编译OSG库、搭建基础开发环境和移植测试Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/133943583 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

mybatis自定义类型控制器(TypeHandler)处理将字符串处理为集合

1. 问题&#xff1a; 假设这么一个场景 localurl里面的值大概这样&#xff1a;dwad21.jpg,dwad22.jpg,dwad.23.jpg 是一个字符串 如果我在sql表中有一个字段&#xff08;local_url&#xff09;是本地图片资源的多个url字符串拼接值。我想在java后端中不进行额外的转换就取值加…

华硕灵耀X双屏pro(UX8402Z)原装Windows11系统恢复安装方法及其教程

华硕灵耀X双屏pro&#xff08;UX8402Z&#xff09;原装Windows11系统恢复安装方法及其教程 第一步&#xff1a;自备原装系统swm/esd/wim/iso等用PE安装还原的系统文件&#xff0c;或者拥有或者售后zip工厂恢复安装包&#xff08;6个底包&#xff1a;EDN.KIT.OFS.HDI.SWP.TLK&a…

【LeetCode:86. 分隔链表 | 链表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

2023年中国档案信息化发展历程、竞争格局及行业市场规模分析[图]

档案信息化是以网络、计算机、信息技术为手段&#xff0c;以档案资源为对象&#xff0c;以档案工作为依托&#xff0c;以档案管理学最新理论为指导&#xff0c;按照信息社会和国家档案行政管理部门的要求、开展档案的收集、整理、保管、开发和利用的现代化管理过程。 档案信息化…

Node.js、Vue的安装与使用(Linux OS)

Vue的安装与使用&#xff08;Linux OS&#xff09; Node.js的安装Vue的安装Vue的使用 操作系统&#xff1a;Ubuntu 20.04 LTS Node.js的安装 安装Node.js Node.js官方下载地址 1.选择合适的系统架构&#xff08;可通过uname -m查看&#xff09;版本安装 2.下载文件为tar.xz格…

1 如何入门TensorFlow

近年来人工智能的火爆吸引了很多人&#xff0c;网上相关的热门课程报名的人很多&#xff0c;但是坚持下去的人却少。那些晦涩的原理没有一定知识的积累很难能理解。 如果你对人工智能感兴趣&#xff0c;且想利用人工智能去实现某项功能&#xff0c;而不是对人工智能本身感兴趣&…

Vue3 + Echarts(5.x) 实现中国地图

Echarts展示地图 效果图 安装 npm install echarts默认安装的是 5.x 版本 在这个版本中的引入方式必须是下面这种方法 import * as echarts from echarts源码 在echarts5.x版本中&#xff0c;已经不再提供地图数据&#xff0c;所以需要我们自己手动下载&#xff0c;我这里…

Qt扫盲-QTextCodec理论总结

QTextCodec理论总结 一、概述二、编码支持三、使用四、创建自己的编解码器类 一、概述 QTextCodec 是Qt提供的一个管理字符串编码的功能&#xff0c;他可以在不同编码方式中来回转换&#xff0c;在文件读取的时候、格式编码转换的时候用处很大。Qt使用Unicode 编码来存储、绘制…

探索未来的视觉革命:卷积神经网络的崭新时代(一)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…