【一题多解】插入回文串——典型的动态规划区间模型

2023-11-06

插入回文串——典型的动态规划区间模型

题目:给定一个长度为n(n <= 1000)的字符串A,求插入最少多少个字符使得它变成一个回文串。

之前我做的DP的问题,大多都是二维的或者一维的,今天就讲下这道典型的一维区间模型。

附上之前写过的二维一维DP模型:

过河问题

最长递增子序列

最长回文子序列

过河卒


问题分析:

一开始看到这个题目的时候,最先让我想到的是之前写过的一道题目最长回文子序列(其实是可以用那道题的二维DP思路解这道题,后面会补充说明,但我先讲解下区间模型)

也许你并不知道如何通过插入最少的字符来使原序列变成一个回文序列,这时候我门不妨换个角度思考下(这也是在做题遇到没思路时的一个小技巧),你肯定知道如何增添字符让一个回文串依旧是回文串,或者让一个回文串不再是回文串。

在这之前,我们先要了解回文序列的特征,当一个序列是回文序列时,如果这个时候我们一定要对其进行增添的操作时,当且仅当在序列的正中间添加一个或者在两边对称添加相同的字符时它还是回文序列,其他的情况(不对称添加或者单个添加)都会使这个回文序列失去回文序列。

也许你会问,这个和题目有什么关系呢?别急,待我慢慢叙说。

我们知道DP的本质是将大问题化为一个个的小问题去解决,那么区间模型就是把大区间逐渐化为小区间去解决,由于回文串的特征,故这个区间是对称缩小的,就是从两边逐渐缩小到中间,而不是单一方向的缩小,如从左缩到右或者从右缩到左(这句话的意思就是有些区间模型是单方向缩小的咯)。

嗯,还是以例子来说明吧。

abc,bad

这个序列要如何修改呢?

假设s[i]表示第i个字符。(从1开始计数)

长度并不长,第一眼看上去也许你会尝试多种可能的情况,对比后得出答案,但我现在带你用DP来解下这道题目

刚刚我们说过这道题要从两边向中间缩,

也许你不知道s[1]到s[7]这串序列的解法,但你不妨先判断下s[1]和s[7]它们各自的值是否相等,如果相等,不就符合我们刚刚所说的回文序列的特征吗?左右刚好对称

如果不相等,因为我们的目标是要让它变成回文,故我们要进行增添操作,刚刚我们又讲过

当且仅当在序列的正中间添加一个或者在两边对称添加相同的字符时它还是回文序列

故我们有两个选择,一个是

在s[1]的左边添加一个值,使它与s[7]一样,

或者在s[7]的右边添加一个值,使它与s[1]一样

选那种呢?当然是选添加最少的那种啦?

也许你会问,这两种情况不是都添加一个值吗?为什么会有多少的问题。

嗯,小朋友,你再思考下,现在我们是不是值判断了最左和最右是否构成了回文的结果啊,中间还有s[2]到s[6]我么没有判断呢~

可以先暂停思考一下,自己设计DP状态转移方程,下面给答案:

dp[i][j] = min( dp[i+1][j]+1, dp[i][j-1]+1  )

代码分析:

#include <bits/stdc++.h>
using namespace std;

char s[1002];
int dp[501][1001];

int palindrome( int i, int j )
{
	if ( i>=j )
		return 0;
	if ( s[i] != s[j] )
		dp[i][j] = min( palindrome(i+1,j)+1, palindrome(i,j-1)+1  );
	else
		dp[i][j] = palindrome(i+1,j-1);
		

	return dp[i][j];

}

int main()
{
	scanf ( "%s", s+1 );
	int len = strlen(s+1);
	
	cout << palindrome(1,len) << endl;

}

其实上面的代码只能通过一半的测试点,剩下一半的测试点会因为超时而不通过,我在之前的博客有分析递归超时的原因以及解决方法和优化方案,这里就不赘述了,没看过的朋友可以先看看
递归超时怎么办?递归与递推的区别?递归的优化之道

对的,上面有太多重复计算的过程了,可以用记忆化减少计算次数,代码如下:

#include <bits/stdc++.h>
using namespace std;

char s[1002];
int dp[600][1001];

int palindrome( int i, int j )
{
	if ( i>=j )
		return 0;
	if ( dp[i][j] >= 0 )
		return dp[i][j];
	if ( s[i] != s[j] )
		dp[i][j] = min( palindrome(i+1,j)+1, palindrome(i,j-1)+1  );			
	else
		dp[i][j] = palindrome(i+1,j-1);			
		

	return dp[i][j];

}

int main()
{
	memset(dp,-1,sizeof(dp));
	scanf ( "%s", s+1 );
	int len = strlen(s+1);
	

	cout << palindrome(1,len) << endl;

}

另一种记忆化写法:

#include <bits/stdc++.h>
using namespace std;

char s[1100];
int dp[1100][1100];
int mark[1100][1100];
	
int palindrome( int i, int j )
{
	if ( i>=j )
		return 0;
	if ( mark[i][j] == 1 )
		return dp[i][j];
	if ( s[i] != s[j] )
	{
		dp[i][j] = min( palindrome(i+1,j)+1, palindrome(i,j-1)+1  );
		mark[i][j] = 1;
	}			
	else
	{
		dp[i][j] = palindrome(i+1,j-1);
		mark[i][j] = 1;	
	}
				
		

	return dp[i][j];

}

int main()
{		
	scanf ( "%s", s+1 );
	int len = strlen(s+1);
	

	cout << palindrome(1,len) << endl;

}

方案二:二维DP

在开头的时候就点了一下,可以用传统的二维DP来做,整体思路就是先求出改字符串的最大回文子序列,保证这个结构不变,然后对那些不对称的零散的字符进行对称添加就好了,关于如何求最大回文子序列,之前有专门写文章说过,证明过程也很详细,这里就不赘述了,如果还是不懂,可以看下这篇文章。代码也在下面,同样AC了。

最长回文子序列

#include <bits/stdc++.h>
using namespace std;

char s[1001];
char reverse_s[1001];
int dp[1001][1001];

int main()
{
	scanf ( "%s", s+1 );
	

	int len = strlen(s+1);
	int i, j;
	for ( i=1; i<=len; i++ )
	{
		reverse_s[i] = s[len-i+1];
	}
	
	for ( i=1; i<=len; i++ )
		for ( j=1; j<=len; j++ )
			if ( reverse_s[i] == s[j] )
				dp[i][j] = dp[i-1][j-1]+1;
			else
				dp[i][j] = max( dp[i-1][j], dp[i][j-1] );
				
	cout << len-dp[len][len] << endl;
	return 0;

}

写在后面:

为了确保大家能够看懂这篇文章,我尽量每字每句都详细讲解,给出证明和解释,真心希望你能够在阅读此篇文章后从中多多少少有所收获。因此每篇文章我都投入了大量的时间和精力,去举例,去说明,去分析,如果你觉得这篇文章写的还不错,点赞、关注和收藏一键三连就是对我最大的鼓励啦,我以后也会写更多更高质量的文章!也欢迎一起讨论指出文章可能存在的逻辑问题~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【一题多解】插入回文串——典型的动态规划区间模型 的相关文章

随机推荐

  • php基于哈希算法出现的强弱比较漏洞

    文章目录 前言 哈希算法 php与哈希算法 php弱比较 0X01 0X02 php强比较 前言 文章同步于我的个人博客https quan9i top phpcompare 在看一些ctf简单题时发现大多用到了 和 这些 因此对其进行总结
  • 源码分析【ReentrantLock】原理

    ReentrackLock底层原理 ReentrackLock介绍 非公平锁VS公平 非公平锁 公平锁 可打断VS不可打断 不可打断 默认 可打断模式 锁超时 条件变量 如何在synchronized和ReentrantLock之间进行选择
  • Python中的retry

    1 通过语言特性实现 for i in range 0 100 while True try do stuff except SomeSpecificException continue break 2 通过第三方库实现 pip insta
  • 前瞻洞察|DoH,隐蔽隧道又添利器,强盾在何方?

    DoH这个词对于很多安全从业人员并不是个新词 但对其前世今生能洞若观火的却不多 本期前瞻洞察将从DNS的隐私与安全问题出发 讲述DoH为什么诞生 DoH的出现到底利弊几何 对其弊端如何应对 为了便于读者理解 对于 何为隐蔽隧道 DoH如何成
  • loguru使用parse解析压缩日志文件(以zip为例)

    loguru保存到zip压缩文件 使用如下代码保存日志文件到zip压缩文件中 from loguru import logger import sys logger remove logger add my cal record filte
  • easyui 表头动态生成

    使用EasyUI实现列不固定的表格 需要引入easyUi中的jQuery easyui min js easyui css icon css 步骤如下 jsp页面 1 新建一个准备放table的div容器 div div 2 在页面加载好后
  • 【netty】Netty堆外内存泄露排查盛宴

    1 概述 转载 Netty堆外内存泄露排查盛宴 2 导读 Netty 是一个异步事件驱动的网络通信层框架 用于快速开发高可用高性能的服务端网络框架与客户端程序 它极大地简化了 TCP 和 UDP 套接字服务器等网络编程 Netty 底层基于
  • jQuery事件冒泡#change(fn)事件结合应用案例--校验用户名是否存在

    事件冒泡即当触发内部节点元素时 同时会触发外部与之关联的节点事件 取消事件冒泡 return false blur fn blur 与change 区别 1 blur fn 失去焦点 触发每一个匹配元素的blur事件 2 change fn
  • 硬件系统工程师宝典(4)-----传输过程的信号要如何描述?

    各位同学大家好 欢迎继续做客电子工程学习圈 今天我们继续来讲这本书 硬件系统工程师宝典 上篇我们说到为实现信号的有效传输 需要保证信号波形的完整和信号时序的完整 并且知道了从时域 频域两个角度去分析信号 那么 在传输过程的信号要如何描述 就
  • OpenGL案例2----keyBoard键盘交互和鼠标交互

    include
  • Docker安装MySQL并配置my.cnf

    1 创建一个临时的mysql 以便复制出my cnf等数据 docker run restart always d v opt data mysql var lib mysql p 3306 3306 name test mysql e M
  • 二进制、十进制、十六进制数值对照表

  • Pandas 日期处理:生成及去除工作日与节假日

    Pandas 日期处理 生成工作日与节假日 Pandas 提供了许多日期处理功能 使得处理时间序列数据变得容易 本文将介绍如何使用 Pandas 生成工作日和节假日 在进行实际操作前 请确保已安装了 Pandas 库 安装方法如下 pip
  • 音视频总结(1) -- 主流音视频平台研究与比较

    虽然本科专业就是图像通信 但是自己真正的从无到有 从0到1的主导和实现一个音视频平台 实现移动互联网时代的音视频通信却是在十几年之后 音视频的使用场景包括视频会议 直播和点播等 下面是对市场上已有产品的研究与调研 流行音视频产品 维基百科上
  • C/C++服务器和客户端交互笔记

    C C 服务器开发 网络与通信Socket Socket通信三要素 通信的目的地址 使用的端口号 http 80 smtp 25 使用的传输协议 TCP UDP nslookup xx 可以查询xx网址的IP地址 Socket通信模型 te
  • 关于qmake报错

    qmake could not find a Qt installation of 或 qmake could not exec usr lib x86 64 linux gnu qt4 bin qmake 或 qmake could no
  • UE5 MediaPlayer无法正确播放视频

    StreamMediaSource 播放流媒体源 流媒体源 Stream Media Source 是一种资源 允许你在虚幻引擎5 UE5 中流送支持的 URL格式视频 定义流后 你可以将其加载并使用 媒体播放器 资源在UE4中播放 并可
  • QT笔记- QTreeView设置三态setAutoTristate() 树形视图自动复选框——源码分享

    说明 Qt中函数QStandardItem setAutoTristate 无实际功能 仅作为一个布尔标记 若要实现自动三态复选框功能 需要自行代码构建 本文通过编写两个派生类 完成了这个功能 类源码和一个示例如下 源码 自动三态item
  • 《人工智能狂潮》读后感——什么是人工智能?(一)

    从本专栏开始 作者正式研究Python深度学习 神经网络及人工智能相关知识 前一篇文章详细讲解了无监督学习Autoencoder的原理知识 然后用MNIST手写数字案例进行对比实验及聚类分析 本篇文章将分享 人工智能狂潮 书籍内容 包括人工
  • 【一题多解】插入回文串——典型的动态规划区间模型

    插入回文串 典型的动态规划区间模型 题目 给定一个长度为n n lt 1000 的字符串A 求插入最少多少个字符使得它变成一个回文串 之前我做的DP的问题 大多都是二维的或者一维的 今天就讲下这道典型的一维区间模型 附上之前写过的二维一维D