【算法突击】动态规划系列 (一)| 程序员面试 | 最大子数组和 | 最长递增子序列 | 最长公共子序列

news2024/11/27 22:35:58

【算法突击】动态规划系列 (一)| 程序员面试 | 最大子数组和 | 最长递增子序列 | 最长公共子序列

文章目录

  • 【算法突击】动态规划系列 (一)| 程序员面试 | 最大子数组和 | 最长递增子序列 | 最长公共子序列
  • 1. 最大子数组和
    • 1.1 题目描述
    • 1.2 解题思路
    • 1.3 代码实现
  • 2. 最长递增子序列
    • 2.1 题目描述
    • 1.2 解题思路
    • 1.3 代码实现
  • 3. 最长公共子序列
    • 3.1 题目描述
    • 3.2 解题思路
    • 3.3 代码实现

1. 最大子数组和

1.1 题目描述

给你一个整数数组 nums ,请你找出一个具有最大元素和 的连续子数组(子数组最少包含一个元素),返回其最大和(子数组 是数组中的一个连续部分)。

1.2 解题思路

(1)穷举法

以数组nums[-2,1,-3,4,-1,2,1,-5,4] 为例,该数组长度为9,设定子数组[i,j](其中j≥i,i≥0, i<9)为最大和的子数组。直接两个嵌套的for循环即可得出答案,时间复杂度是O(n^2)。复杂度太高,是否还有优化空间?

(2)动态规划

以数组nums [-2,1,-3,4,-1,2,1,-5,4] 为例,设定数组f[i]第i位元素结尾时,子数组中最大的元素和(i≥0, i<9),我们接下来寻找一下这个例子中蕴藏的规律。

  • 当指针i=0时

显然f[0] = nums[0],当数组中只有一个元素时,其连续子数组之和就是这个元素;
在这里插入图片描述

  • 当指针i=1时

当前数组nums中只有[-2,1]两个元素,显然-2 + 1 < 1,所以f[1] = nums[1];
在这里插入图片描述

  • 当指针i=2时

nums={-2, 1, -3},由于已经知道了f[1],所以只需比较nums[2] + f[1] 和 当前 nums[2]的大小即可决定在f[2]的大小,故f[2]=nums[2] + f[1] ;
在这里插入图片描述

  • 当指针i=3时

f[3] = nums[3]
在这里插入图片描述

  • 以此类推,当指针i=8时

此时已经计算出数组中全部元素的最大连续和了,发现f[6]是最大的。
在这里插入图片描述

基于上述的推导过程不难发现,f[i]只和f[i-1]和nums[i]的值有关系,且f[0]有初始值,我们将上面分析出的规律总结成数学表达式的形式,如下:

f [ i ] = { n u m s [ 0 ] , 当i=0时 m a x { n u m s [ i ] , n u m s [ i ] + f [ i − 1 ] } , 当i>0时 f[i] = \begin{cases} nums[0], & \text{当i=0时} \\ max\{nums[i], nums[i] + f[i-1]\}, & \text{当i>0时} \\ \end{cases} f[i]={nums[0],max{nums[i],nums[i]+f[i1]},i=0i>0

这个就是动态规划中最重要的状态转移方程,有了这个东西我们可以根据已有的简单结论推导出复杂场景下的结果。另外可以看到这里只需要一次遍历即可解决问题,时间复杂度降到了O(n)。

1.3 代码实现

	public int maxSubArray(int[] nums) {
		int[] f = new int[nums.length];  // 以第i个元素结尾的 最大连续子数组之和
		f[0] = nums[0];
		int max = nums[0];
		for (int i = 1; i < nums.length; i++) {
			f[i] = Math.max(nums[i], nums[i] + f[i - 1]);
			max = Math.max(f[i], max);
		}
		return max;
	}

2. 最长递增子序列

2.1 题目描述

给你一个整数数组 nums ,找到其中最长严格递增子序列长度

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

输入:nums = [10,9,2,5,3,7,101,18]

输出:4

解释:最长递增子序列是 [2,3,7,101],因此长度为 4

示例 2:

输入:nums = [0,1,0,3,2,3]

输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]

输出:1

1.2 解题思路

对于这个题,建模的时候我们将f[i]定义成“第i位元素结尾时,当前数组中最大递增序列长度”,其中i为指针。

以输入nums = [10,9,2,5,3,7,101,18]为例,推导一下整个f数组的构建过程:

  • 当指针i=0时

当前数组中只有一个元素,nums[0],即当前递增子序列长度恒定为1;
在这里插入图片描述

  • 当指针i=1时

当前nums[1] < nums[0],f[1] = 1;
在这里插入图片描述

  • 当指针i=2时

同上,f[2]=1
在这里插入图片描述

  • 当指针i=3时

nums[3] > nums[2], f[3] = f[2] + 1;
在这里插入图片描述

  • 当指针i=4时
    这个时候会发现一个问题,因为nums[4] < nums[3],但是由于题目规则允许跳跃元素,且nums[4] > nums[2],此时的f[4] = f[2] + 1 = 2。由于这个特殊的规则,我们必须再加入一个回溯指针j,在nums[i] < nums[i] - 1时,往i - 1之前进行回溯,取最大的长度f[j] + 1,才能得到当前第i个元素结尾时最长的递增序列长度。在i=4时,j=2即可得到f[4]的正确值。
    在这里插入图片描述

  • 以此类推,当指针i=7时

此时的回溯指针j=5时,可以使得f[7]最大,f[7] = f[5] + 1。
在这里插入图片描述

基于上述的推导过程不难发现,f[i]和f[i-1]之间的关系并不固定,我们必须引入i的回溯指针j才能求出正确结果,把上述规律总结成数学表达式为:

f [ i ] = { 1 , 当i=0时 m a x { f [ j ] + 1 } , 当i>0时,j<i f[i] = \begin{cases} 1 , & \text{当i=0时} \\ max\{f[j] + 1\}, & \text{当i>0时,j<i} \\ \end{cases} f[i]={1,max{f[j]+1},i=0i>0时,j<i

1.3 代码实现

	public int lengthOfLIS(int[] nums) {
    	int[] f = new int[nums.length];
    	int max = 1;
    	// 初始化
    	for(int i=0;i<nums.length;i++) {
    		f[i] = 1;
    	}
      // 状态转移方程
    	for(int i=1;i<nums.length;i++) {
    		for(int j=i-1;j<i;j++) {
    			if(nums[i] > nums[j] && f[i] < f[j] + 1){
    				f[i] = f[j] + 1;
    			}
    		}
    		max = Math.max(max, f[i]);
    	}
    	return max;
    }

3. 最长公共子序列

3.1 题目描述

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:

它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

输入:text1 = “abcde”, text2 = “ace”

输出:3

解释:最长公共子序列是 “ace” ,它的长度为 3

示例 2:

输入:text1 = “abc”, text2 = “abc”

输出:3

解释:最长公共子序列是 “abc” ,它的长度为 3

示例 3:

输入:text1 = “abc”, text2 = “def”

输出:0

解释:两个字符串没有公共子序列,返回 0

3.2 解题思路

为了简单起见,我们将字符串text1称为A串,text2称为B串。

这个题,我们需要在两个不同的字符串中来回比较,所以我们的f[i]不能再是一维的,设f[i][j]为A串中以第i个元素结尾,B串中以第j个元素结尾时的最长公共子序列长度。以text1 = “abcde”, text2 = "ace"为例,推导f数组的构建过程:

  • 当指针i=0, j=0时

因为A[i] = B[j],所以f[i][j] = 1
在这里插入图片描述

更进一步,可以推导出所有f[0][j]和f[i][0]的值
在这里插入图片描述

  • 当指针i=1, j=1时

A[1] ≠ B[2],所以f[1][2] = f[1][0]
在这里插入图片描述

  • 当指针i=1, j=2时

A[1] ≠ B[2],所以f[1][2] = f[1][1]
在这里插入图片描述

  • 当指针i=2, j=1时

A[2] = B[1], f[2][1] = f[1][0]+ 1
在这里插入图片描述

  • 当指针i=3, j=1时

A[3] ≠ B[1], f[3][1] = f[2][1]+ 1,要注意的是,这里有两种选择{abcd, a},{abc, ac},选其中最大的即可
在这里插入图片描述

  • 当指针i=4, j=2时

A[4] = B[2], f[4][2] = f[3][1]
在这里插入图片描述

基于上述的推导过程不难发现,初始化过程之外,匹配的时候需要分两种情况讨论,当A[i] = B[j]时,f[i][j]=f[i-1][j-1] + 1;当A[i] ≠ B[j]时,f[i][j]有两种取值情况,f[i-1][j]与f[i][j-1],取两者中的最大值即可。其数学表达式为:

f [ i ] [ j ] = { 1 , 当i=0且j=0时 m a x { f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] } , 当A[i] != B[j]时 f [ i − 1 ] [ j − 1 ] + 1 , 当A[i]=B[i]时 f[i][j] = \begin{cases} 1 , & \text{当i=0且j=0时} \\ max\{f[i-1][j], f[i][j-1]\}, & \text{当A[i] != B[j]时} \\f[i-1][j-1] + 1 , & \text{当A[i]=B[i]时} \\ \end{cases} f[i][j]= 1,max{f[i1][j],f[i][j1]},f[i1][j1]+1,i=0j=0A[i] != B[j]A[i]=B[i]

3.3 代码实现

public int longestCommonSubsequence(String text1, String text2) {
		char[] A = text1.toCharArray();
		char[] B = text2.toCharArray();
		int[][] f = new int[A.length][B.length];
		// 初始化
		f[0][0] = A[0] == B[0] ? 1 : 0;
		for (int i = 1; i < A.length; i++) {
			f[i][0] = A[i] == B[0] ? 1 : f[i - 1][0];
		}
		for (int j = 1; j < B.length; j++) {
			f[0][j] = A[0] == B[j] ? 1 : f[0][j - 1];
		}
		// 状态转移方程
		for (int i = 1; i < A.length; i++) {
			for (int j = 1; j < B.length; j++) {
				if (A[i] != B[j]) {
					f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]);
				} else {
					f[i][j] = f[i - 1][j - 1] + 1;
				}
			}
		}
		return f[A.length - 1][B.length - 1];
	}

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

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

相关文章

C 语言零基础入门教程(十三)

函数指针 函数指针是指向函数的指针变量。 通常我们说的指针变量是指向一个整型、字符型或数组等变量&#xff0c;而函数指针是指向函数。 函数指针可以像一般函数一样&#xff0c;用于调用函数、传递参数。 函数指针变量的声明&#xff1a; typedef int (*fun_ptr)(int,i…

ATAC-seq分析:Annotating Peaks(9)

1. 注释开放区域 将已识别的无核小体区域与基因组特征&#xff08;如基因和增强子&#xff09;相关联通常很有趣。 一旦注释到基因或增强子的基因&#xff0c;我们就可以开始将 ATACseq 数据与这些基因的特征相关联。 &#xff08;功能注释、表达变化、其他表观遗传状态&#x…

瑞吉外卖实战

https://blog.csdn.net/weixin_43715214/category_12022798.html大佬记录项目介绍day01功能架构&#xff08;1&#xff09;用户层本项目中在构建系统管理后台的前端页面&#xff0c;我们会用到H5、Vue.js、ElementUI等技术。而在构建移动端应用时&#xff0c;我们会使用到微信小…

计算机网络连环炮40问

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

Day09 C++STL入门基础知识六——deque容器 构造函数-赋值操作-大小操作-插入删除-数据存储-排序操作【全面深度剖析+例题代码展示】

无人问津的日子&#xff0c;我为自己喝彩&#xff01; 文章目录1. 基本概念1.1 功能1.2 与vector区别1.3 图解1.4 内部工作原理1.5 deque 容器的迭代器也是支持随机访问的2. 构造函数2.1 函数原型2.2 代码展示2.3 测试结果2.4 小think2.4.1 小问题2.4.2 思路2.4.3 修改2.4.4 测…

从零开始的数模(六)python在高等数学和线性代数中的应用

科学计算设计数值计算和符号计算&#xff0c;在python中作基础数值计算用numpy和scipy工具库&#xff0c;作符号运算用sympy工具库 sympy工具库–符号运算 符号运算基本知识 1.利用symbols函数创建符号变量构造多个符号变量时中间以空格分隔 2.利用符号变量创建表达式 3.利用…

Hive整合HBase,操作HBase表

Hive over HBase原理 Hive与HBase利用两者本身对外的API来实现整合&#xff0c;主要是靠HBaseStorageHandler进行通信&#xff0c;利用 HBaseStorageHandler&#xff0c;Hive可以获取到Hive表对应的HBase表名&#xff0c;列簇以及列&#xff0c;InputFormat和 OutputFormat类&…

STS:Surround-view Temporal Stereo for Multi-view 3D Detection——论文笔记

参考代码&#xff1a;None 1. 概述 介绍&#xff1a;这篇文章提出的方法是对LSS中深度估计部分进行改进&#xff0c;其改进的点是在深度估计部分引入立体匹配去估计周视相机下的深度信息&#xff0c;其中立体匹配使用前后视频帧进行构建&#xff08;可以看作是时序信息的使用&…

node-sass安装失败的解决方案

Nodejs 新版安装过程需要安装node-sass模块&#xff0c;开始一直无法安装成功&#xff0c;网上找了很多方法都无法解决&#xff0c;找了很久才找到的解决方案。 1.1 node-sass安装前准备 Option 2: Install dependencies and configuration manually Install Visual C Build E…

Python处理zip压缩文件

文章目录ZipFile对象写入压缩文件读取和解压缩常用属性ZipInfoZipFile对象 顾名思义&#xff0c;zipfile是处理zip文件的模块&#xff0c;其中最重要的类是ZipFile&#xff0c;其构造函数为 ZipFile(file, moder, compressionZIP_STORED, allowZip64True, compresslevelNone,…

使用VGG网络训练发生错误RuntimeError: CUDA out of memory解决方案:

问题在使用VGG网络训练Mnisist数据集时&#xff0c;发生错误RuntimeError: CUDA out of memory. Tried to allocate 392.00 MiB (GPU 0; 2.00 GiB total capacity; 1.45 GiB already allocated; 0 bytes free; 1.47 GiB reserved in total by PyTorch) If reserved memory is &…

发布详解 | Flutter 3.7 稳定版发布

新年伊始&#xff0c;由 Flutter 3.7 正式版来「打头阵」&#xff01;我们与整个 Flutter 社区成员们继续在 Flutter 3.7 中优化了框架&#xff0c;包括创建自定义菜单栏和层叠式菜单、更好的国际化工具支持、新的调试工具以及其他功能和特性等。新的稳定版里&#xff0c;我们在…

JUC面试(十二)——AQS

AQS juc.locks包下 AbstractQueuedSynchronizer&#xff0c;抽象的队列同步器 aqs是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石&#xff0c; 通过内置的FIFO队列来完成资源获取线程的排队工作&#xff0c;并通过一个int类变量表示持有锁的状态&#x…

极限运算法则——“高等数学”

各位CSDN的uu们你们好啊&#xff0c;今天&#xff0c;小雅兰学习的内容是极限运算法则 回顾 无穷小的极限运算法则 定理1&#xff1a;两个无穷小的和是无穷小 定理2&#xff1a;有界函数与无穷小的乘积是无穷小 极限的四则运算法则 定理3 定理4 定理5&#xff1a;极限的保序性…

实现自己的数据库二

一 前言上次数据库支持了一个测试表的插入和查询&#xff0c;但是数据全部保存到磁盘中的&#xff0c;如果程序重启后&#xff0c;数据都会全部丢了&#xff0c;所以需要持久化到磁盘上&#xff0c;像sqlite一样&#xff0c;简单的将数据库的数据保存到一个磁盘文件上。二 实现…

【BBuf的CUDA笔记】六,总结 FasterTransformer Encoder(BERT) 的cuda相关优化技巧

这里总结 FasterTransformer Encoder(BERT) 的cuda相关优化技巧 解读&#xff1a;https://github.com/NVIDIA/FasterTransformer/blob/main/docs/bert_guide.md &#xff0c;优化点解读之前是翻译了下 Faster Transformer BERT 的文档&#xff0c;然后省略了运行样例等环节&…

【Datewhale一起吃瓜 Task4】啃瓜第五章

支持向量机 任务&#xff1a;找到超平面 在样本空间中&#xff0c;找到最好的超平面把样本分开&#xff0c;即找到正中间的超平面 满足 该超平面 分开了两类该超平面 最大化支持向量间隔该超平面处于 间隔中间&#xff0c;到所有支持向量距离相等 如何找&#xff1a;表示出…

从聚水潭到金蝶云星空通过接口集成数据

从聚水潭到金蝶云星空通过接口集成数据数据源系统:聚水潭聚水潭成立于2014年&#xff0c;创始人兼CEO骆海东拥有近三十年传统及电商ERP的研发和实施部署经验。聚水潭创建之初&#xff0c;以电商SaaSERP切入市场&#xff0c;凭借出色的产品和服务&#xff0c;快速获得市场的肯定…

【论文简述】Attention-Aware Multi-View Stereo(CVPR 2020)

一、论文简述 1. 第一作者&#xff1a;Keyang Luo 2. 发表年份&#xff1a;2020 3. 发表期刊&#xff1a;CVPR 4. 关键词&#xff1a;MVS、代价体、注意力机制、正则化 5. 探索动机&#xff1a; However, the feature matching results from different channels are usual…

仿写Dubbo-MyRpc

基础 在仿写Dubbo之前&#xff0c;需要了解一些技术&#xff0c;像Java反射&#xff0c;Java代理&#xff0c;Java Socket以及Dubbo相关概念。 项目结构 项目gitee地址&#xff1a;https://gitee.com/AGi_R/framework my-common 整个项目的公共资源库。存放一些公共的注解&…