028动态规划之字符串DP——算法备赛 - 实践

news/2026/1/18 11:10:17/文章来源:https://www.cnblogs.com/ljbguanli/p/19498024

028动态规划之字符串DP——算法备赛 - 实践

字符串DP

回文子串个数

问题描述

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

原题链接

思路分析

定义dp[i][j]表示子串[i,j]是否是一个回文串,它可以由dp[i+1,j-1]得到.

代码

int countSubstrings(string s) {
int n=s.size();
vector<vector<bool>>dp(n,vector<bool>(n));int ans=0;for(int j=0;j<n;j++){for(int i=0;i<=j;i++){if(s[i]==s[j]&&(j-i<=2||dp[i+1][j-1])){  //dp[0,j-1][j-1]都计算过dp[i][j]=true;ans++;}}}return ans;}

中心扩展法

代码

int countSubstrings(string s) {
int n=s.size();
int ans=0;
for(int j=0;j<n;j++){
int l=j,r=j;  //回文串为奇数时
while(l>=0&&r<n&&s[l]==s[r]){
ans++;
l--;r++;
}
l=j,r=j+1;  //回文串为偶数时
while(l>=0&&r<n&&s[l]==s[r]){
ans++;
l--;r++;
}
}
return ans;
}

变成回文串的最少插入字符

蓝桥杯2016年省赛题

问题描述

给定一个字符串s,问最少在s中插入多少个字符,能使s变成回文串。

原题链接

思路分析

对于给定的字符串s,如果其已经是回文串了就是不用再插入了,直接输出0。

定义dp,dp[l][r]表示子串[l,r]最少需要插入的字符数,枚举l,r判断s[l]是否等于s[r]

因为计算dp[l][r]要先计算dp[l+1][r]dp[l][r-1]dp[l+1][r-1],可以采用dfs记忆化搜索较为直观地求解。

代码

#include <bits/stdc++.h>using namespace std;string str;vector<vector<int>>dp;int dfs(int l,int r){if(l>=r) return 0;if(dp[l][r]!=-1) return dp[l][r];if(str[l]==str[r]){dp[l][r]=dfs(l+1,r-1);}else{dp[l][r]=min(dfs(l+1,r),dfs(l,r-1))+1;}return dp[l][r];}int main(){// 请在此输入您的代码cin>>str;int n=str.size();dp=vector<vector<int>>(n,vector<int>(n,-1));cout<<dfs(0,n-1);  //记忆化搜索return 0;}

最大回文子串

问题描述

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

原题链接

Manacher 算法

思路

首先只考虑字符串为奇数的字符串,后续再处理偶数字符串。

定义一个新概念臂长,表示中心扩展算法向外扩展的长度。如果一个位置的最大回文字符串长度为 2 * length + 1 ,其臂长为 length

定义i,i从0遍历到s.size()-1;定义一个j,j为遍历过程中 j 的右臂达到最大右边界(记为right)。

当在位置 i 开始进行中心拓展时,我们可以先找到 i 关于 j 的对称点 2 * j - ij-( i - j ) )。那么如果点 2 * j - i 的臂长等于 n,我们就可以知道,点 i 的臂长至少为 min(j + length - i, n)(优化的关键点)。那么我们就可以直接跳过 i 到 i + min(j + length - i, n) 这部分,从 i + min(j + length - i, n) + 1 开始拓展。

在这里插入图片描述

如何处理长度为偶数的回文字符串呢?

我们可以通过一个特别的操作将奇偶数的情况统一起来:我们向字符串的头尾以及每两个字符中间添加一个特殊字符 #,比如字符串 aaba 处理后会变成 #a#a#b#a#

那么原先长度为偶数的回文字符串 aa 会变成长度为奇数的回文字符串 #a#a#,而长度为奇数的回文字符串 aba 会变成长度仍然为奇数的回文字符串 #a#b#a#,我们就不需要再考虑长度为偶数的回文字符串了。

在最后记录最终结果是时在去掉“#”即可

代码

int expand(const string& s, int left, int right) {  //求[left,right]的中心点的臂长
while (left >= 0 && right < s.size() && s[left] == s[right]) {
--left;
++right;
}
return (right - left - 2) / 2;  //此时的right,left比所求值多扩展了一步,所以要-2
}
string longestPalindrome(string s) {
int start = 0, end = -1;
string t = "#";
for (char c: s) {
t += c;
t += '#';
}
t += '#';
s = t;
vector<int> arm_len;int right = -1, j = -1;  //定义初始值为-1,可省去起始时的分情况讨论for (int i = 0; i < s.size(); ++i) {int cur_arm_len;  //臂长if (right >= i) {  //i在右边界内,或可利用之前求得的记录求解int i_sym = j * 2 - i;  //寻找i关于j对称的点  j-(i-j)int min_arm_len = min(arm_len[i_sym], right - i); //点i的最少臂长cur_arm_len = expand(s, i - min_arm_len, i + min_arm_len);  //核心} else {  //i不在右边界内,直接求臂长cur_arm_len = expand(s, i, i);}arm_len.push_back(cur_arm_len);  //臂长记录if (i + cur_arm_len > right) {j = i;  //更新最大右边界的中心点right = i + cur_arm_len;  //记录右边界}if (cur_arm_len * 2 + 1 > end - start) {  //更新历史最大值start = i - cur_arm_len;  //更新目标的左边界end = i + cur_arm_len;  //更新目标的右边界}}string ans;for (int i = start; i <= end; ++i) {if (s[i] != '#') {ans += s[i];}}return ans;}

编辑距离

问题描述

给你两个单词 word1word2请返回将 word1 转换成 word2 所使用的最少操作数

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

原题链接

思路分析

定义dp[n+1][m+1]dp[i][j]表示将word1中前 i 个字符转换为word2中前 j 个字符所需的最小操作数。dp[n][m]就是答案

一般情况下dp[i][j]=min{dp[i-1][j],dp[i][j-1],dp[i][j-1]}+1

min中的三个数+1分别代表以下三种操作

  1. dp[i-1][j]+1,在word1的前i-1个字符转换后,删除第i个字符
  2. dp[i][j-1]+1,在word1的前 i 个字符转换后,再插入一个字符
  3. dp[i-1][j-1]+1,在word1的前 i-1个字符转换后,替换第i个字符

特殊情况 word[i-1]==word[j-1] (也就是word第i个字符与word第j个字符相等时) dp[i][j]可以直接等于dp[i-1][j-1]

dp[i][j]=min{dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]}

代码将两种情况做了合并处理

代码

int minDistance(string word1, string word2) {
int n=word1.size(),m=word2.size();
vector<vector<int>>dp(n+1,vector<int>(m+1));for(int i=0;i<=n;i++) dp[i][0]=i;for(int i=0;i<=m;i++) dp[0][i]=i;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){dp[i][j]=min(min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;if(word1[i-1]==word2[j-1])dp[i][j]=min(dp[i][j],dp[i-1][j-1]);}}return dp[n][m];}

接龙序列

在这里插入图片描述

原题链接

思路分析

求删除最少的个数,相当于求最长的接龙序列长度

问题便转换为求最长的接龙序列长度

定义dp[10] 其中dp[i]表示当前所有数组成的最后末尾数字为i的最长接龙序列长度,每次更新dp后同时更新历史最长接龙序列长度,最后更新后的结果就是答案。

#include <iostream>#include <string>using namespace std;int dp[10]={0};  //dp[i]存储的是末位数字为i的最长接龙序列。int main(){// 请在此输入您的代码int n;cin>>n;string s;  //用字符串接收数据,方便求首尾数位数字。本题不关心具体s是什么数值,只关心首尾数字。int m=0;for(int i=0;i<n;++i){cin>>s;int x=s[0]-'0',y=s[s.size()-1]-'0';dp[y]=max(dp[x]+1,dp[y]);  //对于每个尾数为y的s,可选择接在尾数为x的s的后面,或者不接。取决于能否最大化。m=max(m,dp[y]);  //更新历史最值}cout<<n-m<<endl;return 0;}

子2023

蓝桥杯2023年国赛题

问题描述

在这里插入图片描述

原题链接

代码

string nums;
for(int i=1;i<=2023;i++){
string t=to_string(i);
for(int j=0;j<t.size();j++){
if(t[j]=='0'||t[j]=='2'||t[j]=='3'){  //预处理数据源,将非0,2,3的数字都排除
nums+=t[j];
}
}
}
int n=nums.size();
vector<long long>dp(4);for(int i=0;i<n;i++){if(nums[i]=='2'){dp[0]++;  //‘2’子序列的个数,逢‘2’ +1dp[2]+=dp[1];  //‘202’子序列的个数,逢‘2’+前面‘20’的子序列的个数}else if(nums[i]=='0'){dp[1]+=dp[0];  //‘20’子序列的个数,逢‘0’+前面‘2’的子序列个数}else dp[3]+=dp[2];  //‘2023’子序列的个数,逢‘3’+前面‘202’的子序列的个数}cout<<dp[3];

对字符串进行删除操作的最多次数

问题描述

给你一个长度为 n 的字符串 source ,一个字符串 pattern 且它是 source 的 子序列 ,和一个 有序 整数数组 targetIndices ,整数数组中的元素是 [0, n - 1]互不相同 的数字。

定义一次 操作 为删除 source 中下标为 idx 的一个字符,且需要满足:

  • idxtargetIndices 中的一个元素。
  • 删除字符后,pattern仍然是source的一个 子序列。

执行操作后 不会 改变字符在 source 中的下标位置。比方说,如果从 "acb" 中删除 'c' ,下标为 2 的字符仍然是 'b'

请你返回 最多 可以进行多少次删除操作。

子序列指的是在原字符串里删除若干个(也可以不删除)字符后,不改变顺序地连接剩余字符得到的字符串。

原题链接

思路分析

定义 ff[i][j]表示P(j)S(i)的子序列,所能删除的最大次数,P(j)表示pattern前j个字符组成的字符串,S(i)表示source前i个字符组成的字符串。

将f中元素初始都为INT_MIN,可使前面有j+1source[i] == pattern[j]的情况, f[i][j+1] 才会为非负数 f[i + 1][j+1] = f[i][j+1] + is_del才有为正数(才有意义)

数组多一层,可免去i=0,j=0的特殊情况判断。

代码

int maxRemovals(string source, string pattern, vector<int>& targetIndices) {unordered_set<int> st(targetIndices.begin(), targetIndices.end());int n = source.length(), m = pattern.length();vector<vector<int>> f(n + 1, vector<int>(m + 1, INT_MIN));f[0][0] = 0;for (int i = 0; i < n; i++) {int is_del = st.count(i);  //tergetIndices存在该下标f[i + 1][0] = f[i][0] + is_del;for (int j = 0; j < min(i + 1, m); j++) {f[i + 1][j + 1] = f[i][j + 1] + is_del;//S[i]至少存在子序列P[j+1],f[i][j + 1]才不是负数if (source[i] == pattern[j]) {  //等于的话,多一种选择。f[i + 1][j + 1] = max(f[i + 1][j + 1], f[i][j]);}}}return f[n][m];}//作者:灵茶山艾府

时间复杂度:O(mn)

空间复杂度:O(mn)

空间优化

由于targetIndices是有序的数组,可以用一个指针寻找大于等于i的元素,

由于f[i]只与f[i-1]有关,所以可以用一维数组自我滚动优化空间。

代码

int maxRemovals(string source, string pattern, vector<int>& targetIndices) {int m = pattern.length();vector<int> f(m + 1,INT_MIN);f[0] = 0;int k = 0;for (int i = 0; i < source.length(); i++) {if (k < targetIndices.size() && targetIndices[k] < i) {k++;}int is_del = k < targetIndices.size() && targetIndices[k] == i;for (int j = min(i, m - 1); j >= 0; j--) {f[j + 1] += is_del;if (source[i] == pattern[j]) {f[j + 1] = max(f[j + 1], f[j]);}}f[0] += is_del;}return f[m];}//作者:灵茶山艾府

时间复杂度:O(mn)

空间复杂度:O(m)

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

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

相关文章

研究生写论文必备的3款降AI工具,导师都说自然 - 还在做实验的师兄

研究生论文对学术规范和表达自然度要求更高,普通降AI工具容易改出「机翻味」。本文推荐3款导师认可的降AI工具:嘎嘎降AI(学术味保留好,达标率99.26%)、比话降AI(可降至0%,有退款保障)、AIGCleaner(英文论文专…

手把手教你降论文AI率:从检测到修改的完整操作指南 - 还在做实验的师兄

这篇教程带你走完降AI的全流程:检测定位问题、工具处理、人工精修、验证达标。核心工具推荐嘎嘎降AI(达标率99.26%)。全程约1小时,新手也能搞定。手把手教你降论文AI率:从检测到修改的完整操作指南TL;DR:这篇教程…

职业院校智慧校园评价指标体系如何构建?这份指南请收好

✅作者简介&#xff1a;合肥自友科技 &#x1f4cc;核心产品&#xff1a;智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

论文AI率太高被退回?5招教你快速解决 - 还在做实验的师兄

论文被退回说AI率太高,别慌!这篇文章教你5招快速解决:了解AI率高的真正原因、避开3个常见误区、3个有效的手动修改技巧、借助专业工具快速降AI、最后再做一轮检查。按这个流程走,基本都能解决问题。论文AI率太高被…

深聊江南电缆官方销售热线,电缆选购有哪些要点? - 工业品牌热点

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家标杆电缆制造企业,为工程采购、项目选型提供客观依据,助力精准匹配适配的电缆供应伙伴。 TOP1 推荐:无锡江南电缆有限公司 推荐指数:★★★★★ | 口碑评分…

DeepSeek写的论文怎么降AI?这6款工具亲测有效 - 还在做实验的师兄

DeepSeek写的论文AI率动辄90%以上,直接提交必翻车。实测嘎嘎降AI能把AI率从95%降到9%,3分钟处理完,4.8元/千字。知网要求严的话用比话降AI,承诺降到15%以下否则退款。DeepSeek写的论文怎么降AI?这6款工具亲测有效…

导师严选2026 AI论文软件TOP8:MBA毕业论文写作全解析

导师严选2026 AI论文软件TOP8&#xff1a;MBA毕业论文写作全解析 2026年MBA论文写作工具测评&#xff1a;为何需要一份专业榜单&#xff1f; MBA学位论文的撰写不仅是学术能力的体现&#xff0c;更是对研究方法、逻辑思维和数据分析的综合考验。随着AI技术在学术领域的广泛应用…

题目1112:C语言考试练习题_一元二次方程

#include<iostream> #include<iomanip> #include<cmath> using namespace std; int main(){double a,b,c;cin>>a>>b>>c;double x1(-b(pow(b*b-4*a*c,0.5)))/2*a;//不可以写为1/2&#xff0c;一定是0.5&#xff0c;不可以是b^2,一定是b*b d…

049.二维差分

一维差分 对于原始数组a[] 通过d[i]=a[i]-a[i-1]初始化出d[]差分数组 对差分数组进行若干次修改 // 在[l,r]上加k void change(int l,int r,int k){d[l]+=k;d[r+1]-=k; }最后update得到最终的a[] void update(int n){f…

2025年本地市场热门重型回弹仪品牌推荐,智能非金属超声检测仪/超声波回弹仪/数显碳化深度尺/高强回弹仪回弹仪供应商推荐榜单 - 品牌推荐师

随着我国基础设施建设的持续深化与既有建筑安全评估需求的日益增长,作为混凝土强度无损检测的关键设备,重型回弹仪的市场关注度显著提升。行业正经历从传统机械式向数字化、智能化、高精度方向的转型。然而,面对市场…

融智学形式本体论:一种基于子全域与超子域的统一认知架构

融智学形式本体论&#xff1a;一种基于子全域与超子域的统一认知架构摘要本文正式提出并系统阐述 “融智学形式本体论” 。它以三个不可再分的元子&#xff08;物理、意义、文法&#xff09;为基底&#xff0c;构建一个称为 “分层集合范畴” 的数学结构&#xff0c;实现了对物…

动态电压恢复器(DVR)模型 Matlab/simulink 质量过硬, 可用于治理电能质量问...

动态电压恢复器&#xff08;DVR&#xff09;模型 Matlab/simulink 质量过硬&#xff0c; 可用于治理电能质量问题&#xff1a;仿真总时长0.7s&#xff0c;DVR始终接入&#xff0c;具体如下&#xff1a; 0.1-0.2s治理电压暂降&#xff1b; 0.3-0.4s治理电压暂升&#xff1b; 0.…

2026年国内可靠的全自动超声波清洗机厂家哪家靠谱,单臂超声波清洗机/晶圆清洗机,全自动超声波清洗机公司联系方式 - 品牌推荐师

近年来,随着制造业对精密清洗需求的持续攀升,全自动超声波清洗机凭借高效、环保、一致性强的技术优势,成为汽车零部件、半导体、精密五金等行业的核心设备。然而,市场供应商鱼龙混杂,技术实力、服务能力与定制化水…

MATLAB环境下基于数据驱动的随机子空间(SSI-DATA)和协方差驱动的随机子空间(SSI...

MATLAB环境下基于数据驱动的随机子空间(SSI-DATA)和协方差驱动的随机子空间(SSI-COV)的结构模态参数识别方法&#xff0c;可用于土木&#xff0c;航空航天&#xff0c;机械等领域。 本品为程序&#xff0c;已调通&#xff0c;可直接运行。一、系统概述 本系统是一套基于MATLAB开…

Apache 详解(在 Ubuntu 24 中安装和配置 Apache,超详细)

零散知识讲解 目录零散知识讲解站点配置和全局配置的区别www-data 用户介绍什么是进程的上下文切换?TCP 连接的三个阶段客户端和服务器通信的过程开启 AcceptFilter 和关闭 AcceptFilter的区别在 Ubuntu 24 中安装和配…

Invicti Standard v26.1.0 发布 - 企业级 Web 应用与 API 安全

Invicti Standard v26.1.0 for Windows - 企业级 Web 应用与 API 安全Invicti Standard v26.1.0 for Windows - 企业级 Web 应用与 API 安全 Invicti (formerly Netsparker) | Web Application and API Security for E…

4.4 虚拟人口型驱动:让静态图像开口说话的魔法

4.4 虚拟人口型驱动:让静态图像开口说话的魔法 引言 在前三节中,我们学习了虚拟人的视觉外观生成、扩散模型与ControlNet技术以及声音克隆技术。现在,我们来到了让虚拟人真正"活起来"的关键环节——口型驱动技术。这项技术能够让静态的虚拟人图像根据语音内容同…

leetcode 881. Boats to Save People 救生艇

Problem: 881. Boats to Save People 救生艇 解题过程 排序&#xff0c;然后查找可以配对的&#xff0c;而且右上界是不断缩小的&#xff0c;用到了状态数组 优化版本只需要求出可以配对的&#xff0c;然后总数减去配对数量 Code class Solution { public:int numRescueBoats…

5.2 多模态OCR架构:Donut、TrOCR、LayoutLMv3全面对比

5.2 多模态OCR架构:Donut、TrOCR、LayoutLMv3全面对比 引言 在上一节中,我们回顾了OCR技术的发展历程,从传统的模板匹配方法到现代的深度学习和生成式AI技术。随着多模态学习的兴起,OCR技术也迎来了新的发展机遇。现代多模态OCR架构不仅能够识别文本内容,还能理解文档的…

5.1 OCR技术进化史:从传统方法到生成式AI突破

5.1 OCR技术进化史:从传统方法到生成式AI突破 引言 光学字符识别(Optical Character Recognition, OCR)是人工智能领域的一个重要分支,它使得计算机能够从图像中识别和提取文本信息。从早期的模板匹配到现代的深度学习方法,OCR技术经历了数十年的发展和演进。 在本节中…