当前位置: 首页 > news >正文

C++动态规划算法的应用:得到 K 个半回文串的最少修改次数 原理源码测试用例

本文涉及的基础知识点

动态规划

题目

得到 K 个半回文串的最少修改次数
给你一个字符串 s 和一个整数 k ,请你将 s 分成 k 个 子字符串 ,使得每个 子字符串 变成 半回文串 需要修改的字符数目最少。
请你返回一个整数,表示需要修改的 最少 字符数目。
注意:
如果一个字符串从左往右和从右往左读是一样的,那么它是一个 回文串 。
如果长度为 len 的字符串存在一个满足 1 <= d < len 的正整数 d ,len % d == 0 成立且所有对 d 做除法余数相同的下标对应的字符连起来得到的字符串都是 回文串 ,那么我们说这个字符串是 半回文串 。比方说 “aa” ,“aba” ,“adbgad” 和 “abab” 都是 半回文串 ,而 “a” ,“ab” 和 “abca” 不是。
子字符串 指的是一个字符串中一段连续的字符序列。
示例 1:
输入:s = “abcac”, k = 2
输出:1
解释:我们可以将 s 分成子字符串 “ab” 和 “cac” 。子字符串 “cac” 已经是半回文串。如果我们将 “ab” 变成 “aa” ,它也会变成一个 d = 1 的半回文串。
该方案是将 s 分成 2 个子字符串的前提下,得到 2 个半回文子字符串需要的最少修改次数。所以答案为 1 。
示例 2:
输入:s = “abcdef”, k = 2
输出:2
解释:我们可以将 s 分成子字符串 “abc” 和 “def” 。子字符串 “abc” 和 “def” 都需要修改一个字符得到半回文串,所以我们总共需要 2 次字符修改使所有子字符串变成半回文串。
该方案是将 s 分成 2 个子字符串的前提下,得到 2 个半回文子字符串需要的最少修改次数。所以答案为 2 。
示例 3:
输入:s = “aabbaa”, k = 3
输出:0
解释:我们可以将 s 分成子字符串 “aa” ,“bb” 和 “aa” 。
字符串 “aa” 和 “bb” 都已经是半回文串了。所以答案为 0 。
参数范围:
2 <= s.length <= 200
1 <= k <= s.length / 2
s 只包含小写英文字母。

分析

第一轮:vector<vector> m_aDCenger[2][101]; 四维数组:第一维,0表示奇数长度,1表示偶数长度。 第二维:d。第三维:中心。第四维半长长度。值记录变成:半回文需要改变的次数。
第二轮:int m_vNeedNum[200][201] 记录s[left,r)变成半回文需要改变的次数。
第三轮:三层循环,第一层循环:枚举k,第二层循环枚举s[0,j]。第三轮循环枚举m,[0,m]和(m,j]。
三轮时间复杂度都是:O(nnn);

核心代码

class Solution {
public:
int minimumChanges(string s, int k) {
m_c = s.length();
Init(s);
Init2(s);
vector pre(m_c);//pre[j]将s[0,j]拆分成i-1个子字符串,这些子串全部半回文的需要改变的字符数
for (int j = 0; j < m_c; j++)
{
pre[j] = m_vNeedNum[0][j + 1];
}
for (int i = 2; i <= k; i++)
{
vector dp(m_c);
for (int j = i - 1; j < m_c; j++)
{//拆分成[0,m]和(m,j]([m+1,j+1)),前者i-1个子串,后者一个子串
int iMin = INT_MAX;
for (int m = max(0,i-2); m < j; m++)
{
const int cur = pre[m] + m_vNeedNum[m + 1][j + 1];
iMin = min(iMin, cur);
}
dp[j] = iMin;
}
pre.swap(dp);
}
return pre.back();
}
void Init(std::string& s)
{
for(int i = 0 ; i < 2 ; i++ )
for (int j = 0; j <= m_c / 2; j++)
{
m_aDCenger[i][j].assign(m_c+1, vector((m_c+1)/2+1));
}
for (int d = 1; d <= m_c/2; d++)
{
for (int center = 0; center < m_c; center++)
{
int halfLen = 1;
while ((center + d* (halfLen-1) < m_c) && (center - d* (halfLen - 1) >= 0) )
{
m_aDCenger[1][d][center][halfLen] = m_aDCenger[1][d][center][halfLen - 1] +(s[center + d * (halfLen - 1)] != s[center - d * (halfLen - 1)]) ;
halfLen++;
}
}
for (int center = 0; center < m_c; center++)
{//偶数半回文
int halfLen = 1;
while ((center + d * halfLen < m_c) && (center - d * (halfLen - 1) >= 0) )
{
m_aDCenger[0][d][center][halfLen] = m_aDCenger[0][d][center][halfLen - 1] + (s[center + d * halfLen] != s[center - d * (halfLen - 1)]);
halfLen++;
}
}
}
}
void Init2(std::string& s)
{
memset(m_vNeedNum, sizeof(m_vNeedNum), 0);
for (int left = 0; left < m_c; left++)
{
for (int r = left + 1; r <= m_c; r++)
{
const int subLen = r - left;
int iNeed = 1000*1000;
for (int d = 1; (d * d <= subLen)&&(subLen >1); d++)
{
if (0 != subLen % d)
{
continue;
}
{
const int iCurNeed = DoLeftRightD(left, r, d);
iNeed = min(iNeed, iCurNeed);
}
if(d >1 )
{
const int iCurNeed = DoLeftRightD(left, r, subLen/d);
iNeed = min(iNeed, iCurNeed);
}
}
m_vNeedNum[left][r] = iNeed;
}
}
}
int DoLeftRightD(int left,int r,int d)
{
const int subLen = r - left;
const int len = subLen / d;
const auto& arr = m_aDCenger[len % 2];
const int midIndex = (len-1)/2;
int iCurNeed = 0;
for (int begin = 0; begin < d; begin++)
{
const int center = left + begin + midIndex * d;
iCurNeed += arr[d][center][(len + 1) / 2];
}
return iCurNeed;
}
int m_c;
vector<vector> m_aDCenger[2][101];
int m_vNeedNum[200][201];
};

测试用例

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
assert(v1[i] == v2[i]);
}
}

template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}

int main()
{
//string s = “bbacccbbaabbddddddddddddddddddddddddddddddddddddddddddddbbacccbbaabbdddddddddddddddddddddddddddddddddddddddddddddbbbacccbbaabbddddddddddddddddddddbbacccbbaabbdddddddddddddddddddddddddddddddddddddddddd”;
string s = “abcac”;
int k = 2;
Solution slu;
auto res = slu.minimumChanges(s,k);
Assert(1, res);

//CConsole::Out(res);

}

小幅改进

空间复杂度降为O(nn)。一,预处理的时候,利用当前d,更新m_vLeftRightToNeedChange。更新完旧d的信息就不需要了。二,不需要分奇数长度和偶数长度回文。长度本身可以分奇偶了。
Init2共3层循环,时间复杂度是:O(n
n),第三层第四层,时间复杂度共O(n)。第三层循环的时间复杂度是: n/d,第四层时间复杂度是d。

新代码

class Solution {
public:int minimumChanges(string s, int k) {m_c = s.length();		m_vLeftRightToNeedChange.assign(m_c, vector<int>(m_c, m_iNotMay));for (int d = 1; d <= m_c / 2; d++){Init(s,d);Init2(s,d);}vector<int> pre(m_c);//pre[j]将s[0,j]拆分成i-1个子字符串,这些子串全部半回文的需要改变的字符数for (int j = 0; j < m_c; j++){pre[j] = m_vLeftRightToNeedChange[0][j];}for (int i = 2; i <= k; i++){vector<int> dp(m_c);for (int j = i - 1; j < m_c; j++){//拆分成[0,m]和(m,j]([m+1,j])),前者i-1个子串,后者一个子串int iMin = INT_MAX;for (int m = max(0,i-2); m < j; m++){const int cur = pre[m] + m_vLeftRightToNeedChange[m + 1][j];iMin = min(iMin, cur);}dp[j] = iMin;}pre.swap(dp);}return pre.back();}void DoCenter(int center, bool bEven,int d,const string& s){int left = center, r = left + d*bEven;if (r >= m_c){return;}m_vDLeftRightToNeedChange[left][r] = (s[left] != s[r]);left -= d;r += d;while ((r < m_c) && (left >= 0 )){m_vDLeftRightToNeedChange[left][r] = m_vDLeftRightToNeedChange[left + d][r - d] + (s[left] != s[r]);left -= d;r += d;}}void Init(std::string& s,int d ){m_vDLeftRightToNeedChange.assign(m_c, vector<int>(m_c, m_iNotMay));		for (int center = 0; center < m_c; center++){DoCenter(center, true, d,s);DoCenter(center, false, d,s);}}void Init2(std::string& s, int d){		for (int left = 0; left < m_c; left++){for (int iSubLen = 2; left+ iSubLen*d <= m_c ; iSubLen++){const int r = left + iSubLen * d;int iNeedChange = 0;for (int j = 0; j < d; j++){iNeedChange += m_vDLeftRightToNeedChange[left+j][r - d+j];}m_vLeftRightToNeedChange[left][r - 1] = min(m_vLeftRightToNeedChange[left][r - 1],iNeedChange);}}}	const int m_iNotMay = 1000 * 1000;int m_c;vector<vector<int>> m_vDLeftRightToNeedChange;//m_vDLeftRightToNeedChange[left][r],表示当前d,str[left,left+d....r]是回文的最少次数vector<vector<int>> m_vLeftRightToNeedChange;//m_vLeftRightToNeedChange[left][r]表示s[left,r]变成半回文的最少改变次数
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++动态规划算法的应用:得到 K 个半回文串的最少修改次数 原理源码测试用例

本文涉及的基础知识点 动态规划 题目 得到 K 个半回文串的最少修改次数 给你一个字符串 s 和一个整数 k &#xff0c;请你将 s 分成 k 个 子字符串 &#xff0c;使得每个 子字符串 变成 半回文串 需要修改的字符数目最少。 请你返回一个整数&#xff0c;表示需要修改的 最少…...

Pyside6 QFileDialog

Pyside6 QFileDialog Pyside6 QFileDialog常用函数getOpenFileNamegetOpenFileNamesgetExistingDirectorygetSaveFileName 程序界面程序主程序 Pyside6 QFileDialog提供了一个允许用户选择文件或目录的对话框。关于QFileDialog的使用可以参考下面的文档 https://doc.qt.io/qtfo…...

Leetcode1793. Maximum Score of a Good Subarray

给定一个数组和一个下标 k k k 子数组 ( i , j ) (i,j) (i,j)分数定义为 min ⁡ ( n u m s [ i ] , n u m s [ i 1 ] , ⋯ , n u m s [ j ] ) ∗ ( j − i 1 ) \min\left(nums[i], nums[i 1],\cdots, nums[j]\right)*\left(j-i1\right) min(nums[i],nums[i1],⋯,nums[j])∗(…...

只需五步,在Linux安装chrome及chromedriver(CentOS)

一、安装Chrome 1&#xff09;先执行命令下载chrome&#xff1a; wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm2&#xff09;安装chrome yum localinstall google-chrome-stable_current_x86_64.rpm看到下图中的Complete出现则代表安装…...

第01章-Java语言概述

目录 1 常见DOS命令 常用指令 相对路径与绝对路径 2 转义字符 3 安装JDK与配置环境变量 JDK与JRE JDK的版本 JDK的下载 JDK的安装 配置path环境变量 4 Java程序的编写与执行 5 Java注释 6 Java API文档 7 Java核心机制&#xff1a;JVM 1 常见DOS命令 DOS&#xff08;…...

Spring | Spring Cache 缓存框架

Spring Cache 缓存框架&#xff1a; Spring Cache功能介绍Spring Cache的Maven依赖Spring Cache的常用注解EnableCaching注解CachePut注解Cacheable注解CacheEvict注解 Spring Cache功能介绍 Spring Cache是Spring的一个框架&#xff0c;实现了基于注解的缓存功能。只需简单加一…...

雷达开发的基本概念fft,cfar,以及Clutter, CFAR,AoA

CFAR Constant False-Alarm Rate的缩写。在雷达信号检测中&#xff0c;当外界干扰强度变化时&#xff0c;雷达能自动调整其灵敏度&#xff0c;使雷达的虚警概率保持不变。具有这种特性的接收机称为恒虚警接收机。雷达信号的检测总是在干扰背景下进行的&#xff0c;这些干扰包括…...

什么是大数据测试?有哪些类型?应该怎么测?

随着目前世界上各个国家使用大数据应用程序或应用大数据技术场景的数量呈指数增长&#xff0c;相应的&#xff0c;对于测试大数据应用时所需的知识与大数据测试工程师的需求也在同步增加。 针对大数据测试的相关技术已慢慢成为当下软件测试人员需要了解和掌握的一门通用技术。…...

03-垃圾收集策略与算法

垃圾收集策略与算法 程序计数器、虚拟机栈、本地方法栈随线程而生&#xff0c;也随线程而灭&#xff1b;栈帧随着方法的开始而入栈&#xff0c;随着方法的结束而出栈。这几个区域的内存分配和回收都具有确定性&#xff0c;在这几个区域内不需要过多考虑回收的问题&#xff0c;因…...

1.AUTOSAR的架构及方法论

在15、16年之前,AUTOSAR这个东西其实是被国内很多大的OEM或者供应商所排斥的。为什么?最主要的原因还是以前采用手写底层代码+应用层模型生成代码的方式进行开发。每个供应商或者OEM都有自己的软件规范或者技术壁垒,现在提个AUTOSAR想搞统一,用一个规范来收割汽车软件供应链…...

Kotlin中的List集合

在Kotlin中&#xff0c;List集合用于存储一组有序的元素。List集合分为可变集合&#xff08;MutableList&#xff09;和不可变集合&#xff08;List&#xff09;。本篇博客将分别介绍可变集合和不可变集合&#xff0c;并提供相关的API示例代码。 不可变集合&#xff08;List&a…...

微信小程序WeUI项目weui-miniprogram如何运行起来?

微信小程序WeUI项目weui-miniprogram如何运行起来&#xff1f; 解决方法: 1、下载 https://github.com/wechat-miniprogram/weui-miniprogram 2、在项目根目录weui-miniprogram-master执行以下命令安装依赖&#xff1a; npm install 3、继续执行编译命令&#xff1a; npm r…...

MapReduce编程:检索特定群体搜索记录和定义分片操作

文章目录 MapReduce 编程&#xff1a;检索特定群体搜索记录和定义分片操作一、实验目标二、实验要求及注意事项三、实验内容及步骤 附&#xff1a;系列文章 MapReduce 编程&#xff1a;检索特定群体搜索记录和定义分片操作 一、实验目标 熟悉MapReduce编程涉及的主要类和接口…...

pytorch 入门 (四)案例二:人脸表情识别-VGG16实现

实战教案二&#xff1a;人脸表情识别-VGG16实现 本文为&#x1f517;小白入门Pytorch内部限免文章 参考本文所写记录性文章&#xff0c;请在文章开头注明以下内容&#xff0c;复制粘贴即可 &#x1f368; 本文为&#x1f517;小白入门Pytorch中的学习记录博客&#x1f366; 参…...

数据结构--线性表回顾

目录 线性表 1.定义 2.线性表的基本操作 3.顺序表的定义 3.1顺序表的实现--静态分配 3.2顺序表的实现--动态分配 4顺序表的插入、删除 4.1插入操作的时间复杂度 4.2顺序表的删除操作-时间复杂度 5 顺序表的查找 5.1按位查找 5.2 动态分配的方式 5.3按位查找的时间…...

ChatGPT(1):ChatGPT初识

1 ChatGPT原理 ChatGPT 是基于 GPT-3.5 架构的一个大型语言模型&#xff0c;它的工作原理涵盖了深度学习和自然语言处理技术。以下是 ChatGPT 的工作原理的一些关键要点&#xff1a; 神经网络架构&#xff1a;ChatGPT 的核心是一个深度神经网络&#xff0c;采用了变种的 Tran…...

PostgreSQL 插件 CREATE EXTENSION 原理

PostgreSQL 提供了丰富的数据库内核编程接口&#xff0c;允许开发者在不修改任何 Postgres 核心代码的情况下以插件的形式将自己的代码融入内核&#xff0c;扩展数据库功能。本文探究了 PostgreSQL 插件的一般源码组成&#xff0c;梳理插件的源码内容和实现方式&#xff1b;并介…...

Android常见分区

一、Google官方标准分区 1. Boot分区 包含Linux内核和一个最小的root文件系统(装载到ramdisk中)&#xff0c;用于挂载系统和其他的分区并开始Runtime。正如名字所代表的意思&#xff08;注&#xff1a;boot的意思是启动&#xff09;&#xff0c;这个分区使Android设备可以启动…...

华为鸿蒙4谷歌GMS安装教学

目录 问题描述 参考视频 教学视频1 配套文档 教学视频2 资源包(配套视频1) 设备未经 play 保护机制认证 问题描述 很多国外的最新应用需要再Google商店才能下载比如ChatGPT 华为手机不支持 Google Play 服务的原因主要是由于谷歌服务框架&#xff08;GMS&#xff09;未…...

原型设计工具:Balsamiq Wireframes 4.7.4 Crack

原型设计工具:Balsamiq Wireframes是一种快速的低保真UI 线框图工具&#xff0c;可重现在记事本或白板上绘制草图但使用计算机的体验。 它确实迫使您专注于结构和内容&#xff0c;避免在此过程后期对颜色和细节进行冗长的讨论。 线框速度很快&#xff1a;您将产生更多想法&am…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...