【动态规划 前缀和】2478. 完美分割的方案数

news2025/4/21 14:15:57

本文涉及知识点

划分型dp 动态规划汇总
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

LeetCode 2478. 完美分割的方案数

给你一个字符串 s ,每个字符是数字 ‘1’ 到 ‘9’ ,再给你两个整数 k 和 minLength 。
如果对 s 的分割满足以下条件,那么我们认为它是一个 完美 分割:
s 被分成 k 段互不相交的子字符串。
每个子字符串长度都 至少 为 minLength 。
每个子字符串的第一个字符都是一个 质数 数字,最后一个字符都是一个 非质数 数字。质数数字为 ‘2’ ,‘3’ ,‘5’ 和 ‘7’ ,剩下的都是非质数数字。
请你返回 s 的 完美 分割数目。由于答案可能很大,请返回答案对 109 + 7 取余 后的结果。
一个 子字符串 是字符串中一段连续字符串序列。
示例 1:
输入:s = “23542185131”, k = 3, minLength = 2
输出:3
解释:存在 3 种完美分割方案:
“2354 | 218 | 5131”
“2354 | 21851 | 31”
“2354218 | 51 | 31”
示例 2:
输入:s = “23542185131”, k = 3, minLength = 3
输出:1
解释:存在一种完美分割方案:“2354 | 218 | 5131” 。
示例 3:
输入:s = “3312958”, k = 3, minLength = 1
输出:1
解释:存在一种完美分割方案:“331 | 29 | 58” 。
提示:
1 <= k, minLength <= s.length <= 1000
s 每个字符都为数字 ‘1’ 到 ‘9’ 之一。

划分型dp

预处理

bool bPrime[128] 记录那些字符是质数。
vNotPirme记录所有非质数下标。二维向量lr,记录所有可以划分的区间,左闭右开。如果s[i]不是质数,则s[i]为空。
否则将所有r ∈ \in vNotPrime ,且 r- i +1 >= minLength 的r+1放到s[i]中。只后就是经典的划分性dp。
时间复杂度 : O(n3)超时。

代码

class Solution {
public:
	int beautifulPartitions(string s, int K, int minLength) {
		bool bPrime[128] = { false };
		bPrime['2'] = bPrime['3'] = bPrime['5'] = bPrime['7']=true;	
		const int N = s.length();
		vector<int> notPrime;
		for (int i = 0; i < N; i++) {
			if (bPrime[s[i]]) { continue; }
			notPrime.emplace_back(i);
		}
		vector<vector<int>> lr(N);
		for (int i = 0,j=0; i < N; i++) {
			if (!bPrime[s[i]]) { continue; }
			while ((j < notPrime.size()) && (notPrime[j] - i + 1 < minLength)) {
				j++;
			}
			for (int k = j; k < notPrime.size(); k++) {
				lr[i].emplace_back(notPrime[k] + 1);
			}
		}
		vector<vector<C1097Int<>>> dp(K+1,vector<C1097Int<>>(N + 1));
		dp[0][0] = 1;
		for (int i = 0; i < N; i++) {
			for(int k = 0; k < K ;k++ )
			for (int r : lr[i]) {
				dp[k + 1][r] += dp[k][i];
			}
		}
		return dp.back().back().ToInt();
	}
};

动态规划+前缀和

如果s[0]不是质数或s.back()是质数,无法分割,直接返回0。
分成k段有k-1分割点。
s[i]非质数,s[i+1]是质数,则可以分割。
vSplit记录所有的分割点,为了避免处理边界vSplit = {-1}; 最后追加n-1

动态规划的状态

dp[i][k]表示处理完vSplit[i]的分割k段的方案。 空间复杂度:O(mk) m= vSplit.size()

动态规划的转移方程

dp[i][k] = ∑ j : 0 v S p l i t [ i ] − v S p l i t [ j ] > = m i n L e n g h t \Large\sum_{j:0}^{vSplit[i]-vSplit[j] >= minLenght } j:0vSplit[i]vSplit[j]>=minLenghtdp[j][k-1]
利用前缀和优化,单个状态转移的时间复杂度是:O(1)
时间复杂度:O(mk)

动态规划的填表顺序

i从1到m-1,k从1到K

动态规划的初始值

dp[0][0] = 1 ,其余全为0 .

动态规划的返回值

dp.back().back()

代码

核心代码

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;
	}
	C1097Int  operator/(const C1097Int& o)const
	{
		return *this * o.PowNegative1();
	}
	C1097Int& operator/=(const C1097Int& o)
	{
		*this /= o.PowNegative1();
		return *this;
	}
	bool operator==(const C1097Int& o)const
	{
		return m_iData == o.m_iData;
	}
	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 beautifulPartitions(string s, int K, int minLength) {
		bool bPrime[128] = { false };
		bPrime['2'] = bPrime['3'] = bPrime['5'] = bPrime['7']=true;	
		if ((!bPrime[s[0]]) || (bPrime[s.back()])) { return 0; }
		const int N = s.length();
		vector<int> vSplit = { -1 };
		for (int i = 0; i+1 < N; i++) {
			if (bPrime[s[i]]) { continue; }
			if (bPrime[s[i + 1]]) { vSplit.emplace_back(i); }
		}		
		vSplit.emplace_back(N - 1);
		const int M = vSplit.size();
		vector<vector<C1097Int<>>> dp(M , vector<C1097Int<>>(K + 1));
		dp[0][0] = 1;
		vector<C1097Int<>> vSum(K);
		for (int m = 1, i = 0; m < M; m++) {
			while ((i < M) && (vSplit[m] - vSplit[i] >= minLength)) {
				for (int k = 0; k < K; k++) {
					vSum[k] += dp[i][k];
				}
				i++;
			}
			for (int k = 1; k <= K; k++) {
				dp[m][k] = vSum[k - 1];
			}
		}		
		return dp.back().back().ToInt();
	}
};

单元测试

template<class T1, class T2>
void AssertEx(const T1& t1, const T2& t2)
{
	Assert::AreEqual(t1, t2);
}

template<class T>
void AssertEx(const vector<T>& v1, const vector<T>& v2)
{
	Assert::AreEqual(v1.size(), v2.size());
	for (int i = 0; i < v1.size(); i++)
	{
		Assert::AreEqual(v1[i], v2[i]);
	}
}

template<class T>
void AssertV2(vector<vector<T>> vv1, vector<vector<T>> vv2)
{
	sort(vv1.begin(), vv1.end());
	sort(vv2.begin(), vv2.end());
	Assert::AreEqual(vv1.size(), vv2.size());
	for (int i = 0; i < vv1.size(); i++)
	{
		AssertEx(vv1[i], vv2[i]);
	}
}

namespace UnitTest
{	
	string s;
	int k, minLength;
	TEST_CLASS(UnitTest)
	{
	public:
		TEST_METHOD(TestMethod00)
		{
			s = "23542185131", k = 3, minLength = 2;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(3, res);
		}
		TEST_METHOD(TestMethod01)
		{
			s = "23542185131", k = 3, minLength = 3;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod02)
		{
			s = "3312958", k = 3, minLength = 1;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod03)
		{
			s = "24", k = 1, minLength = 1;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod031)
		{
			s = "24", k = 1, minLength =3;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(0, res);
		}
		TEST_METHOD(TestMethod04)
		{
			s = "783938233588472343879134266968", k = 4, minLength = 6;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(4, res);
		}
		TEST_METHOD(TestMethod05)
		{
			s = "242538614532395749146912679859", k = 1, minLength = 6;
			auto res = Solution().beautifulPartitions(s, k, minLength);
			AssertEx(1, res);
		}
	};
}

扩展阅读

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

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

相关推荐

我想对大家说的话
《喜缺全书算法册》以原理、正确性证明、总结为主。
按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

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

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

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

相关文章

Redis---保证主从节点一致性问题 +与数据库数据保持一致性问题

保证主从节点一致性问题 Redis的同步方式默认是异步的&#xff0c;这种异步的同步方式导致了主从之间的数据存在一定的延迟&#xff0c;因此Redis默认是弱一致性的。 解决&#xff1a; 1.使用Redisson这样的工具&#xff0c;它提供了分布式锁的实现&#xff0c;确保在分布式环…

python自动化运维--DNS处理模块dnspython

1.dnspython介绍 dnspython是Pyhton实现的一个DNS工具包&#xff0c;他几乎支持所有的记录类型&#xff0c;可以用于查询、传输并动态更新ZONE信息&#xff0c;同事支持TSIG&#xff08;事物签名&#xff09;验证消息和EDNS0&#xff08;扩展DNS&#xff09;。在系统管理方面&a…

京东云备案流程图_云主机快速ICP备案_京东云服务器备案问题解答

京东云ICP备案流程&#xff0c;备案包括网站和APP备案&#xff0c;以及备案问题解答FAQ&#xff0c;阿腾云以京东云网站域名备案流程为例&#xff0c;先填写主办单位信息&#xff0c;选择网站备案或APP备案&#xff0c;申请授权码并验证&#xff0c;填写并上传主办单位详细信息…

老挝语翻译成简体中文推荐用什么翻译工具?《老挝语翻译通》App满足你所有翻译需求!

如果你正在找一款支持把老挝语翻译成中文&#xff0c;或者把中文翻译成老挝语的翻译工具&#xff0c;不得不推荐你使用《老挝语翻译通》App&#xff0c;您的随身老挝语翻译官&#xff0c;带您轻松跨越语言障碍。 功能亮点&#xff1a; 实时翻译&#xff1a;中文与老挝语的无缝…

论文阅读【时间序列】DSformer

论文阅读【时间序列】DSformer arxive: DSformer: A Double Sampling Transformer for Multivariate Time Series Long-term Prediction github: MTST 分类&#xff1a;多变量时间序列&#xff08;Multivariate time series&#xff09; 核心观点 多变量时间序列3个维度信息 …

一款十六进制编辑器,你的瑞士军刀!!【送源码】

软件介绍 ImHex是一款功能强大的十六进制编辑器&#xff0c;专为逆向工程师、程序员以及夜间工作的用户设计。它不仅提供了基础的二进制数据编辑功能&#xff0c;还集成了一系列高级特性&#xff0c;使其成为分析和修改二进制文件的理想工具。 功能特点 专为逆向工程、编程和夜…

Appium adb 获取appActivity

方法一&#xff08;最简单有效的方法&#xff09; 通过cmd命令&#xff0c;前提是先打开手机中你要获取包名的APP adb devices -l 获取连接设备详细信息 adb shell dumpsys activity | grep mFocusedActivity 有时获取到的不是真实的Activity 方法二 adb shell monkey -p …

K6 性能测试教程:入门介绍,环境搭建和编写第一个 K6 测试脚本

K6 性能测试教程&#xff1a;入门介绍&#xff0c;环境搭建和编写第一个 K6 测试脚本 这篇文章将带您进入 K6 性能测试的世界。博文内容涵盖了 K6 性能测试的入门知识、环境搭建步骤&#xff0c;以及如何编写您的第一个测试脚本。无论您是初学者还是有经验的性能测试专业人员&…

剪映 v5.5 Pro Vip解锁版:使用指南与注意事项

摘要&#xff1a;本文介绍了剪映Pro VIP解锁版的使用方法&#xff0c;包括安装、测试和使用VIP素材的步骤&#xff0c;以及如何避免误报和保持解锁状态的建议。 正文&#xff1a; 剪映Pro是一款广受欢迎的视频编辑软件&#xff0c;提供了丰富的视频编辑功能和大量高质量的素材…

Python基础001

Python输出语句 print输出字符串 print("中国四大名著&#xff1a;","西游记|","三国演义|","红楼梦|","水浒传") print(6) print(1 1)Python输入语句 input函数 input() input("我的名字是&#xff1a;") p…

javaSE期末练习题

文章目录 前言一、程序控制1.顺序结构问题描述解题思路题解 2.选择结构2.1 题1问题描述解题思路题解 2.1 题2问题描述解题思路题解 3.循环结构3.1 阶乘的求取问题描述解题思路题解 3.2 水仙花数问题描述解题思路题解 二、数组三、类与对象1.类与对象1.1圆类问题描述解题思路题解…

【ES】--Elasticsearch的Nested类型介绍

目录 一、问题现象二、普通数组类型1、为什么普通数组类型匹配不准?三、nested类型四、nested类型查询操作1、只根据nested对象内部数组条件查询2、只根据nested对象外部条件查询3、根据nested对象内部及外部条件查询4、向nested对象数组追加新数据5、删除nested对象数组某一个…

【QT】常用控件|widget|QPushButton|RadioButton|核心属性

目录 ​编辑 概念 信号与槽机制 控件的多样性和定制性 核心属性 enabled geometry ​编辑 windowTiltle windowIcon toolTip styleSheet PushButton RadioButton 概念 QT 控件是构成图形用户界面&#xff08;GUI&#xff09;的基础组件&#xff0c;它们是实现与…

第十四届蓝桥杯省赛C++B组E题【接龙数列】题解(AC)

需求分析 题目要求最少删掉多少个数后&#xff0c;使得数列变为接龙数列。 相当于题目要求求出数组中的最长接龙子序列。 题目分析 对于一个数能不能放到接龙数列中&#xff0c;只关系到这个数的第一位和最后一位&#xff0c;所以我们可以先对数组进行预处理&#xff0c;将…

VMware创建新虚拟机教程(保姆级别)

&#x1f4e2; 续上一篇 最新超详细VMware虚拟机安装完整教程-CSDN博客 &#xff0c;本章将详细讲解VMware创建虚拟机。 一、创建新的虚拟机 点击【创建新的虚拟机】&#xff01; 点击【自定义&#xff08;高级&#xff09;】> 下一步&#xff01; > 默认下一步&#x…

[hive] posexplode生成从去年一月一号,到本月的月时间表

生成从去年一月一号&#xff0c;到本月的月时间表 posexplode用法&#xff1a; lateral view 表别名 as 序号列名,数组中的元素的名 1、生成序列 SELECT time_stamp_fist_day_of_last_year,--去年第一天的时间戳numfrom(SELECTsplit(repeat_o,,) o_array,time_stamp_fist_da…

【2024德国签证】去德国读博士需要申请什么签证?

德国留学签证面签的经过及注意事项 ✨&#xff01;希望我的经验可以帮助大家顺利通过签证&#xff0c;顺利开启德国留学之旅 。记得带上足够的现金和材料哦 &#xff01; 一、选择适合自己的签证类型 在选择签证类型时&#xff0c;一定要根据自己的实际情况来选择合适的签证种…

vmware虚拟机安装openEuler

一、openEuler简介 openEuler是一款开源操作系统。当前openEuler内核源于Linux&#xff0c;支持鲲鹏及其它多种处理器&#xff0c;能够充分释放计算芯片的潜能&#xff0c;是由全球开源贡献者构建的高效、稳定、安全的开源操作系统&#xff0c;适用于数据库、大数据、云计算、…

ESP32CAM物联网教学01

ESP32CAM物联网教学01 拍照 视频 这么小的一个开发板都带上摄像头了&#xff0c;能拍照&#xff1f;能视频吗&#xff1f;现在就跟着我做起来。 初识ESP32CAM 我们到淘宝搜索“ESP32Cam”&#xff0c;就能买到这样一块开发板。 ESP32Cam是双核处理器&#xff0c;提供WIFI和…

监控平台zabbix介绍与部署

目录 1.为什么要做监控 2.zabbix是什么&#xff1f; 3.zabbix 监控原理 4.Zabbix 6.0 新特性 5.Zabbix 6.0 功能组件 6.部署zabbix 6.1 部署 Nginx PHP 环境并测试 6.2 部署数据库 6.3 向数据库导入 zabbix 数据 6.4 编译安装 zabbix Server 服务端 6.5 修改 zabbix…