【C++动态规划 子集状态压缩】2002. 两个回文子序列长度的最大乘积|1869

【C++动态规划 子集状态压缩】2002. 两个回文子序列长度的最大乘积|1869

本文涉及知识点

C++动态规划
位运算、状态压缩、枚举子集汇总

LeetCode2002. 两个回文子序列长度的最大乘积

给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。
请你返回两个回文子序列长度可以达到的 最大乘积 。
子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。
示例 1:

在这里插入图片描述


输入:s = “leetcodecom”
输出:9
解释:最优方案是选择 “ete” 作为第一个子序列,“cdc” 作为第二个子序列。
它们的乘积为 3 * 3 = 9 。
示例 2:
输入:s = “bb”
输出:1
解释:最优方案为选择 “b” (第一个字符)作为第一个子序列,“b” (第二个字符)作为第二个子序列。
它们的乘积为 1 * 1 = 1 。
示例 3:
输入:s = “accbcaxxcxx”
输出:25
解释:最优方案为选择 “accca” 作为第一个子序列,“xxcxx” 作为第二个子序列。
它们的乘积为 5 * 5 = 25 。
提示:
2 <= s.length <= 12
s 只含有小写英文字母。

暴力

v记录所有回文子序列的掩码mask和子系列的长度。(1<<j)&mask 表示此子序列是否包括s[j]。

暴力一

枚举i,j,如果i&j则忽略。i,j ∈ \in ∈[v] 时间复杂度:O(4n)

暴力二

枚举i和 i的反码的子集。时间复杂度:O(3n)

动态规划+记忆化搜索=错误

动态规划的状态表示

dp[i][j][k] 表示s[i…j]中,一个回文序列为k,另一个回文序列的长度。-2,表示未处理;-1,表示不存在长度为k的回文子序列。 空间复杂度:O(nnn)

动态规划的转移方程

len = j-i+1
如果len为1:
cur 代表dp[i][j],cur[0]=1,其它为-1。
如果len为2:
dp[1]=1 如果s[i]= = s[j],则dp[0]=2,否则d[0]=1。其它全为-1。
如果len为3:
如果s[i]= = s[j] cur[k] = dp[i+1][j-1][k]+2
MaxSelf(cur[k],dp[i+1][j][k])
MaxSelf(cur[k],dp[i ][j-1][k])
无论len是多少:
MaxSelf(cur[cur[k]],k)
时间复杂度:O(nnn)

动态规划的初始调用

Rec(0,N-1)

动态规划的返回值

dp[0].back() 值乘以下标的最大值。

错误代码

本解法的假设:最外围的回文对,一定包括所有的回文。比如:AXXYYA
实际上可能是:ABAB ,可以拆分出:AA BB

classSolution{public:intmaxProduct(string s){constint N = s.length(); vector<vector<vector<int>>>dp(N, vector<vector<int>>(N,vector<int>(N+1,-2))); function<void(int,int)> Rec =[&](int i,int j){auto& cur = dp[i][j];if(-2!= cur[0])return;constint len = j - i +1;if(1== len){ cur[0]=1;}elseif(2== len){ cur[0]=1+(s[i]== s[j]); cur[1]=1;}else{Rec(i +1, j -1);Rec(i , j -1);Rec(i +1, j );if(s[i]== s[j]){for(int k =0; k <= N; k++){ cur[k]= dp[i +1][j -1][k]+2;}}for(int k =0; k <= N; k++){ cur[k]=max(cur[k],dp[i+1][j][k]); cur[k]=max(cur[k], dp[i][j-1][k]);}} vector<int>diff(N +2);for(int k =0; k <= N; k++){for(int k1 =0; k1 <= cur[k]; k1++){ cur[k1]=max(cur[k1], k);}}};Rec(0, N -1);int ans =0;for(int i =1; i < N; i++){ ans =max(ans, dp[0].back()[i]* i);}return ans;}};

暴力二代码

核心代码

classSolution{public:intmaxProduct(string s){constint N = s.length();constint MC =1<< N;auto Is =[&](const vector<int>& tmp){for(int i =0; i < tmp.size()/2; i++){if(tmp[i]!= tmp[tmp.size()-1- i])returnfalse;}returntrue;}; unordered_map<int,int> m;for(int i =0; i < MC; i++){ vector<int> tmp;for(int j =0; j < N; j++){if(i &(1<< j))tmp.emplace_back(s[j]);}if(Is(tmp))m[i]= tmp.size();}int ans =1;for(constauto&[i, l1]: m){constint remain = i ^(MC -1);for(int j = remain; j; j =(j -1)& remain){if(m.count(j)){ ans =max(ans, l1 * m[j]);}}}return ans;}};

单元测试

TEST_METHOD(TestMethod1){ string s ="bab";auto res =Solution().maxProduct(s);AssertEx(2, res);}TEST_METHOD(TestMethod2){ string s ="eetcodec";auto res =Solution().maxProduct(s);AssertEx(9, res);}TEST_METHOD(TestMethod11){ string s ="leetcodecom";auto res =Solution().maxProduct(s);AssertEx(9, res);}TEST_METHOD(TestMethod12){ string s ="bb";auto res =Solution().maxProduct(s);AssertEx(1, res);}TEST_METHOD(TestMethod13){ string s ="accbcaxxcxx";auto res =Solution().maxProduct(s);AssertEx(25, res);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步ZEEKLOG学院,听白银讲师(也就是鄙人)的讲解。
https://edu.ZEEKLOG.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.ZEEKLOG.net/lecturer/6176

测试环境

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

Read more

【算法竞赛】C/C++ 的输入输出你真的玩会了吗?

【算法竞赛】C/C++ 的输入输出你真的玩会了吗?

🔭 个人主页:散峰而望 《C语言:从基础到进阶》《编程工具的下载和使用》《C语言刷题》《算法竞赛从入门到获奖》《人工智能AI学习》《AI Agent》 愿为出海月,不做归山云 🎬博主简介 文章目录 * 前言 * 1. OJ(online judge)题目输入情况汇总 * 1.1 单组测试用例 * 1.2 多组测试用例 * 1.2.1 测试数据组数已知 * 1.2.2 测试数据组未知 * 1.2.3 特殊值结束测试数据 * 2. 输入时特殊技巧 * 2.1 含空格字符串的特殊处理方式 * 2.2 数字的特殊处理方式 * 3. scanf/printf 和

By Ne0inhk
【C++指南】vector(二):手把手教你底层原理与模拟实现

【C++指南】vector(二):手把手教你底层原理与模拟实现

.💓 博客主页:倔强的石头的ZEEKLOG主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C++指南》 期待您的关注 文章目录 * 一、引言 * 二、成员变量 * 三、默认成员函数 * 2.1 默认构造函数 * 2.2 析构函数 * 2.3 拷贝构造函数 * 传统写法 * 现代写法 * 2.4 赋值重载函数 * 传统写法 * 现代写法 * 四、元素访问相关 * 3.1 `[]` 重载(非 `const` 版本) * 3.2 `[]` 重载(`const` 版本) * 五、迭代器相关 * 4.1 迭代器类型声明 * 4.

By Ne0inhk
使用现代C++构建高效日志系统的分步指南

使用现代C++构建高效日志系统的分步指南

使用现代C++构建高效日志系统的分步指南 * 1. 确定日志系统的需求和目标 * 2. 设计日志系统的架构 * 3. 实现阶段 * 3.1 实现日志管理器(LogManager) * 3.2 实现日志记录器(Logger) * 3.3 实现日志格式化器(Formatter) * 3.4 实现日志输出器(Outputter) * 3.5 实现日志文件轮转 * 3.6 实现异常处理 * 3.7 实现性能优化 * 4. 测试和验证 * 5. 文档编写 * 6. 总结 在软件开发中,日志系统扮演着关键角色,帮助开发者记录程序运行状态、调试问题以及监控系统性能。使用现代C++构建一个高效且灵活的日志系统,不仅可以提升开发效率,还能增强程序的可维护性和可靠性。以下是构建这样一个日志系统的详细分步指南: 1. 确定日志系统的需求和目标

By Ne0inhk
C++之《程序员自我修养》读书总结(5)

C++之《程序员自我修养》读书总结(5)

《程序员自我修养》读书总结(五) Author: Once Day Date: 2026年2月12日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: 书籍阅读_Once-Day的博客-ZEEKLOG博客 参考文章:《程序员的自我修养》读书笔记 | Zachary’s blog《程序员的自我修养》阅读笔记 - T0fV404 - 博客园读书笔记:《程序员的自我修养》 - 楷哥 - 博客园 文章目录 * 《程序员自我修养》读书总结(五) * 5. Windows PE/COFF 格式 * 5.1 发展历史 * 5.2 mingw-w64 工具链 * 5.

By Ne0inhk