二刷力扣——DP算法(子序列问题)

300. 最长递增子序列

定义是以本元素结尾,所以公式初始化都好弄。但是太慢

class Solution {public int lengthOfLIS(int[] nums) {int n=nums.length;int[] dp = new int[n];//以自己结尾的最长递增子序列dp[0]=1;int maxzi=1;for(int i=1;i<n;++i){dp[i]=1;for(int j=0;j<i;++j){if(nums[i]>nums[j])dp[i]=Math.max(dp[i],dp[j]+1);} maxzi=Math.max(maxzi,dp[i]);}return maxzi;}
}

时间O(n^2),空间O(n)

看题解的优化:

贪心+二分查找:

下面链接说的很清楚:

https://writings.sh/post/longest-increasing-subsequence-revisited

中心思想是:

7bc063999cfd400986b22078eed5da2b.png

所以这里的d数组定义是:d[i]是长度为 i 的递增子序列的最小结尾元素。比如d[2]=6,所以长度为2 的递增子序列里面,结尾元素最小的情况是2。

所以挨个遍历nums数组来更新这个d数组,注意这个d数组是动态的,不像之前动态规划的DP数组,我们得记录他的长度,因为遇到了nums某元素比d最后的结尾还大,就要把这个某元素加入到d数组,扩大d数组。。如果遇到某元素比结尾小,对应d数组定义,那d数组里面肯定有某个值要更新为这个nums[i],这个值是哪个位置?就是把比nums[i]大的&最接近nums[i]的 换成nums[i]。举例下图。遇到3比d 的结尾8要小,所以更新d  的某个元素,找到比3大的&最接近3 的元素是6,so替换。这样保证d[2]=3,对应长度为2的递增子序列的最小结尾元素是3(2、3这个子序列)。

f9ab93768f754176b843a471d640e040.png

所以在过程中,d数组长度不会减少,所以p数组装的nums元素 的个数,就是所要求的最长递增子序列长度,d数组的最后结尾元素代表了一条递增最慢的子序列,所以这个元素的下标,也就是len就是结果。

还有一个解释:

1cbc84e833f4447cb6d9eebf4c8ec244.png

代码注意,没有写==的时候break,所以退出循环一定是left=right+2了,这个时候left的位置就是 比nums[i]大的&最接近nums[i]的 位置,然后直接替换成nums[i]。

len是为了记录包含nums元素 的个数,因为这里没有使用大小可动态变化的集合。

class Solution {public int lengthOfLIS(int[] nums) {int n=nums.length;if(n==0)return 0;int len =1;int[] d = new int[n+1];//长度为i的递增子序列的最小结尾元素d[len]=nums[0];for(int i=1;i<n;++i){if(nums[i]>d[len])//加到结尾去{d[++len]=nums[i];}else//在d[1]-d[len]中找到更改为nums[i]后使得d仍然有序的位置;二分查找更快{int l=1,r=len;//左闭右闭while(l<=r){int mid=(l+r)>>1;if(d[mid]<nums[i]){l=mid+1;}else r=mid-1;}d[l]=nums[i];//替换}}return len;}
}

贪心遍历nums,动态规划遍历dp,给固定大小的数组的每个元素求解值。

674. 最长连续递增序列

可以DP数组,也可以贪心省存储。

贪心:用start记录连续子序列的开头下标,发现连续递增在i 这里断了,就更新start为i(重新一个递增序列)。然后取最大值

class Solution {public int findLengthOfLCIS(int[] nums) {int n=nums.length,maxCount=1;int start=0;for(int i=1;i<n;++i){if(nums[i]<=nums[i-1]){start=i;}maxCount=Math.max(maxCount,i-start+1);}return maxCount; }
}

718. 最长重复子数组

dp[i][j]:如果不想单独初始化的话,dp设置比nums偏移一个坐标,是以nums1[i-1]和nums2[j-1]结尾的子数组最长 长度;不偏移的话是nums1[i]和nums2[j]结尾的子数组最长 长度

公式:如果遍历的两个元素相等,可以在上一个dp[i-1][j-1]的基础上拼接这个元素,所以+1

 

偏移:

class Solution {public int findLength(int[] nums1, int[] nums2) {int m=nums1.length,n=nums2.length;int[][] dp=new int[m+1][n+1];//以nums1[i-1]和nums2[j-1]结尾的子数组最长 长度int maxCount=0;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(nums2[j-1]==nums1[i-1]){dp[i][j]=dp[i-1][j-1]+1;maxCount=Math.max(dp[i][j],maxCount);}}}return maxCount;}
}

还可以用滚动数组实现,注意跟 01背包的滚动数组实现 一样,留下的那个维度只能在内循环,而且得从大到小,因为得是上一轮的值而不是这一轮更新过的值。

而且不相等的时候,要把dp[j]清零,因为二维的时候不操作dp[i][[j]直接就是0;但是一维的时候不操作是继承了上一轮的值,是错的。只要不相等以nums1[i-1]结尾的子数组长度就是0,所以置零。

class Solution {public int findLength(int[] nums1, int[] nums2) {int m=nums1.length,n=nums2.length;int[] dp=new int[m+1];//以nums1[i-1] 结尾的子数组最长 长度int maxCount=0;for(int i=1;i<=n;++i){for(int j=m;j>=1;--j)//留下维度从大到小{if(nums2[i-1]==nums1[j-1]){dp[j]=dp[j-1]+1;maxCount=Math.max(dp[j],maxCount);}else dp[j]=0;   //不相等了,清零}}return maxCount;}
}

 

不偏移:

class Solution {public int findLength(int[] nums1, int[] nums2) {int m=nums1.length,n=nums2.length;int[][] dp=new int[m ][n ];//nums1[i]和nums2[j]结尾的子数组最长 长度int maxCount=0;for(int i=0;i<n;++i){dp[0][i]=nums1[0]==nums2[i]?1:0;maxCount=Math.max(dp[0][i],maxCount);}for(int i=0;i<m;++i){dp[i][0]=nums1[i]==nums2[0]?1:0;maxCount=Math.max(dp[i][0],maxCount);}for(int i=1;i<m;++i){for(int j=1;j<n;++j){if(nums2[j]==nums1[i]){dp[i][j]=dp[i-1][j-1]+1;maxCount=Math.max(dp[i][j],maxCount);}}}return maxCount;}
}

 

1143. 最长公共子序列

dp[i][j]的定义跟上面不一样,这里子序列是不连续的,如果定义还要求是以元素结尾的子序列长度的话,i元素=j元素,dp[i][j]不一定是在dp[i-1][j-1]的基础上+1,就会很麻烦。

所以设置为:text1[0,i-1]和text2[0,j-1]范围内,最长公共子序列的长度.

最后返回的就是dp[m][n]

递推公式:如果相等的话,是在dp[i-1][j-1]的基础上+1。

如果不相等,不能直接跳过,比如abcde,ace,到了abc、ace的时候c!=e,那么dp[3][3]按照定义应该是2,等于dp[3][2]。把abc、ace倒过来,dp[3][3]又=dp[2][3]。A[i-1]和B[j-1]不相等,但是A[i-1]可能和B[j-1]之前的相等,B[j-1]可能和A[i-1]之前的相等,所以要取这两种情况的最大值。

采用跟上提一样的偏移写法:

class Solution {public int longestCommonSubsequence(String text1, String text2) {int m=text1.length(),n=text2.length();int[][] dp=new int[m+1][n+1];//text1[0,i-1]和text2[0,j-1]范围内,最长公共子序列的长度for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(text1.charAt(i-1)==text2.charAt(j-1)){dp[i][j]=dp[i-1][j-1]+1;}else dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);}}return dp[m][n];}
}

同样可以用滚动数组吗?

不行,

c1b233ccbae44bc3b7a8e21d12deb35c.png

dp[i][j]取决于这三个元素,如果用滚动数组,无论行优先,还是列优先遍历,dp[i][j-1]/dp[i-1][j]都会把dp[i][j]覆盖。

1035. 不相交的线

跟上题一样。

392. 判断子序列

编辑距离问题 的入门题目。用上面的方法然后判断dp[m][n]是否=s.length()的话,效率低。

 

所以也可以用双指针做一下:

class Solution {public boolean isSubsequence(String s, String t) {int m=s.length(),n=t.length();int sp=0,tp=0;while(sp<m && tp<n)//sp:s已经匹配了的右边界{if(s.charAt(sp)==t.charAt(tp)){sp++;}tp++;}if(sp==m)return true;return false;}
}

时间O(n)。

有一个进阶问题;

95c0ade49a9d4c50a960ee0a0522aa79.png

力扣题解给出一个跟S 无关的预处理DP数组的做法:dp[i][j] 表示字符串 t 中从位置 i 开始往后字符 j 第一次出现的位置。

递推公式:

e688bf5962aa4d4783628f4c9783d233.png

初始化:有效的下标 i 就是0到n-1,所以多设置一个n ,让dp[n][j]都=n,代表位置不存在。这是判断false的条件。

遍历顺序:得到下标 i到n-1 的第一次位置,所以i 需要从大到小。

class Solution {public boolean isSubsequence(String s, String t) {int m=s.length(),n=t.length();int[][] dp=new int[n+1][26];//字符串 t 中从位置 i 开始往后字符 j 第一次出现的位置//初始化for(int i=0;i<26;++i){dp[n][i]=n;//代表不存在}for(int i=n-1;i>=0;--i)//和s无关,得到dp数组{for(int j=0;j<26;++j){if(t.charAt(i)==j+'a'){dp[i][j]=i;//更新位置}else dp[i][j]=dp[i+1][j];}}//检查S1、S2……int add=-1;for(int i=0;i<m;++i){add=dp[add+1][s.charAt(i)-'a'];//以上一次找到的位置的下一个为起点System.out.println(add);if(add==n)return false;}return true;}
}

过程大概就是:

s = "abc", t = "ahbgdc"

s[0]=a在t[0,n-1]的第一个位置, 是0;

s[1]=b在t[1,n-1]的第一个位置,是2;

s[2]=c在t[3,n-1]的第一个位置,是5;

保证了s各字母在t中找到的位置是递增的,而且这个位置不等于 代表不存在的n,就返回true。

115. 不同的子序列

仔细看题目,在s子序列t 出现的个数。s的子序列是不连续的,t是整个连续的。

定义dp的时候,dp[i][j]表示 在s[0-i](不一定以i结尾)中t[0,j](连续,一定以j结尾)出现的个数。

所以这里dp[i][j]有两种可能:以s[i]结尾和不以s[i]结尾,这是分情况讨论的来源。

1、当s[i-1]==t[j-1],以s[i-1]结尾的话,t[j-1]也定了,所以只能等于不包含这两个相同元素的个数,也就是dp[i-1][j-1];

不以s[i-1]结尾的话,那就是不包含这个元素,所以要找这个元素之前子序列出现个数,即dp[i-1][j]。

递推公式题解中的解释,力扣给的逆序的DP:

e4033dc16c494731b0336577a235b0ff.png

2、当s[i-1]!=t[j-1],肯定只会是不以s[i-1]结尾了,所以让s[0,i-2]和t[0,j-1]匹配,即dp[i-1][j]。

初始化:如果像上面的那样,初始化为0,结果只会是0。先看dp[i][0],s[0-i-1]包含空串的数量,应该为1;而且空串也是空串的子串,所以dp[0][0]=1。dp[0][j],空串包含t[0,j-1]的数量,应该是0。

class Solution {public int numDistinct(String s, String t) {int m=s.length(),n=t.length();int[][] dp=new int[m+1][n+1];//在s[0-i-1](不要求以i结尾)的子序列中t[0,j-1](要以j结尾)出现的个数for(int i=0;i<=m;++i)dp[i][0]=1;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(s.charAt(i-1)==t.charAt(j-1))dp[i][j]=dp[i-1][j ]+dp[i-1][j-1];else dp[i][j]=dp[i-1][j];}}return dp[m][n];}
}

用Java不用取余也能过。

也可以用滚动数组。注意内循环也得从大到小:

fe33b2df35b84cf2a5f6d350ca31fd96.png

class Solution {public int numDistinct(String s, String t) {int m=s.length(),n=t.length();int[] dp=new int[n+1];//在s[0-i-1](不要求以i结尾)的子序列中t[0,j-1](要以j结尾)出现的个数dp[0]=1;for(int i=1;i<=m;++i){for(int j=n;j>=1;--j)//避免覆盖上一轮的{if(s.charAt(i-1)==t.charAt(j-1))dp[j]+=dp[j-1];// else dp[j]=dp[j];}}return dp[n];}
}

583. 两个字符串的删除操作

dp[i][j]定义:1[0—i]、2[0—j ]相等的最小步数。

初始化:根据定义,dp[0][i]应该=i;同理dp[0][j]=j。

2元素相等,那肯定这一步不用删除元素,直接等于之前的dp[i-1][j-1]

2个元素不相等的时候,需要删除,那就有三种情况。删一个有两种,或者两个都删。既然是最小步数,所以取最小值。

class Solution {public int minDistance(String word1, String word2) {int m=word1.length(),n=word2.length();int[][] dp=new int[m+1][n+1];//1[0,i-1]、2[0,j-1]相等的最小步数for (int i = 1; i <= m; i++)dp[i][0] = i;for (int j = 1; j <= n; j++)dp[0][j] = j;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(word1.charAt(i-1)==word2.charAt(j-1)){dp[i][j]=dp[i-1][j-1];}else dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i][j-1])+1,dp[i-1][j-1]+2);}}return dp[m][n];}
}

也不能滚动数组。

 

最后,综合题:

72. 编辑距离

相等和上面是一样的。

不相等:(注意只能操作word1)

增:dp[i][j-1]+1。增了之后新增的和2[j-1]相等。

删:dp[i-1][j]+1

换:dp[i-1][j-1]+1

关于增删情况的解释:

eb1d38ec3cf44a39b2386f20f798d41b.png

所以取这三个的最小值。

class Solution {public int minDistance(String word1, String word2) {int m=word1.length(),n=word2.length();int[][] dp=new int[m+1][n+1];//1[0,i-1]转化成2[0,j-1]for(int i=1;i<=m;++i)dp[i][0]=i;for(int j=1;j<=n;++j)dp[0][j]=j;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(word1.charAt(i-1)==word2.charAt(j-1)){dp[i][j]=dp[i-1][j-1];}else {dp[i][j]=1+Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j]));}}}return dp[m][n];}
}

 

编辑距离总结篇

dp大小一般都设置[m+1][n-1]。dp[i][j]代表1[i-1]和2[j-1] 的关系。是有意义的,比如1、2是字符串的话,dp[0]就涉及到空串。

1、判断子序列:可以DP,可以双指针。

2、不同的子序列:比较难,要把握题目意思,两种情况都要考虑(有/无 s子序列结尾元素)。有的情况是dp几,无的情况是dp几。

b73eaae55aba4746a7a56be7425e1f03.png

有的话是dp[i-1][j-1]。因为i-1和j-1能对上,所以这两个固定了,就不考虑了,只看前面的个数。

无的话是dp[i-1][j],只能确定不考虑i-1,考虑i-2及之前的;j-1不知道和谁对上,所以j-1仍然要考虑,即为dp[i-1][j]

这里的思想和编辑距离是差不多的。

3、两个字符串删除操作

不相等的时候,有三种情况,算好做一点。

3c8723d309504ca196d899e9371461d8.png

4、编辑距离

有了前面的铺垫好做一些了。

27586126b5cc4daeaf5ab94254085cfd.png

要想清楚的仍是 删/增/改 分别对应dp几:

b229fd119be84201967861e2ea53510d.png

 

子数组,子序列问题,dp的定义要思考,要不要以当前元素结尾?结尾的话好递推吗?…

 

 

 

 

 

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

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

相关文章

QT中QDomDocument读写XML文件

一、XML文件 <?xml version"1.0" encoding"UTF-8"?> <Begin><Type name"zhangsan"><sex>boy</sex><school>Chengdu</school><age>18</age><special>handsome</special>&l…

【YOLOv5进阶】——引入注意力机制-以SE为例

声明&#xff1a;笔记是做项目时根据B站博主视频学习时自己编写&#xff0c;请勿随意转载&#xff01; 一、站在巨人的肩膀上 SE模块即Squeeze-and-Excitation 模块&#xff0c;这是一种常用于卷积神经网络中的注意力机制&#xff01;&#xff01; 借鉴代码的代码链接如下&a…

在C#中使用RabbitMQ做个简单的发送邮件小项目 _

前言 好久没有做项目了&#xff0c;这次做一个发送邮件的小项目。发邮件是一个比较耗时的操作&#xff0c;之前在我的个人博客里面回复评论和友链申请是会通过发送邮件来通知对方的&#xff0c;不过当时只是简单的进行了异步操作。那么这次来使用RabbitMQ去统一发送邮件&#x…

vue中路由来回切换页面直接卡死

今天发现一个很严重的问题&#xff0c;项目好不容易做好了&#xff0c;结果页面多了&#xff0c;切换之后卡死。页面所有的交互效果都失效了。 排查了许久的错误原因最后发现原来是路由名称重复了。 如上图当页面跳转到riskdetails详细页面之后&#xff0c;框架则被这个详情页…

node.lib下载失败,手动下载并配置

在无网络环境&#xff0c;或者网络不好的环境&#xff0c;node.lib会下载失败&#xff0c;此时可手动下载并进行配置。 我们以 node16.17.0 为例&#xff1a; 下载地址 分别下载node.lib和headers https://registry.npmmirror.com/-/binary/node/v16.17.0/win-x64/node.lib…

Linux rpm与yum

一、rpm包管理 rpm用于互联网下载包的打包及安装工具&#xff0c;它包含在某些Linux分发版中。它生成具有.RPM扩展名的文件。RPM是RedHat Package Manager (RedHat软件包管理工具&#xff09;的缩写&#xff0c;类似windows的setup.exe&#xff0c;这一文件格式名称虽然打上了R…

办理北京公司注销流程和步骤说明

公司的生命周期是多变的&#xff0c;有时候&#xff0c;业务可能会结束或者出现其他原因&#xff0c;需要注销公司。注销公司是一个复杂的法律过程&#xff0c;需要遵循一系列的步骤和提交特定的材料。下面我们将详细介绍北京注销公司的流程以及需要准备的材料&#xff0c;以帮…

私有云统一多云管理平台主要服务内容

私有云统一多云管理平台&#xff0c;作为企业IT架构现代化的关键组成部分&#xff0c;旨在为企业提供高效、灵活、安全的云计算资源管理解决方案。这类平台通过整合和优化不同云环境(包括私有云、公有云、混合云)的管理&#xff0c;帮助企业打破云孤岛&#xff0c;实现资源的统…

【游戏引擎之路】登神长阶(五)

5月20日-6月4日&#xff1a;攻克2D物理引擎。 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 6月13日-6月20日&#xff1a;攻克《3D图形教程》。 6月21日-6月22日&#xff1a;攻克《Raycasting游戏教程》。 6月23日-6月30日&#xff1a;攻克《Windows游戏编程大师技巧》。 …

【Qwen2部署实战】Qwen2初体验:用Transformers打造智能聊天机器人

系列篇章&#x1f4a5; No.文章1【Qwen部署实战】探索Qwen-7B-Chat&#xff1a;阿里云大型语言模型的对话实践2【Qwen2部署实战】Qwen2初体验&#xff1a;用Transformers打造智能聊天机器人3【Qwen2部署实战】探索Qwen2-7B&#xff1a;通过FastApi框架实现API的部署与调用4【Q…

从任意用户注册到任意密码重置

写在最前面一句话 To be or not to be ,it‘s a question . 哎呀&#xff0c;放错台词了&#xff0c;应该是 true or false , 在最近的测试中遇到了一个很有趣的点 “将 false 改为true ”就可以成功绕过验证码了。 T rue or false &#xff1f;&#xff1f;&#xff1f; …

「51媒体」企业举行新闻发布会,如何邀请媒体到场报道

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 企业举行新闻发布会时&#xff0c;邀请媒体到场报道是一个…

MySQL常用操作命令大全

文章目录 一、连接与断开数据库1.1 连接数据库1.2 选择数据库1.3 断开数据库 二、数据库操作2.1 创建数据库2.2 查看数据库列表2.3 删除数据库 三、表操作3.1 创建表3.2 查看表结构3.3 修改表结构3.3.1 添加列3.3.2 删除列3.3.3 修改列数据类型 3.4 删除表 四、数据操作4.1 插入…

day62--若依框架(基础应用篇)

若依搭建 若依版本 官方 若依官方针对不同开发需求提供了多个版本的框架&#xff0c;每个版本都有其独特的特点和适用场景&#xff1a; 前后端混合版本&#xff1a;RuoYi结合了SpringBoot和Bootstrap的前端开发框架&#xff0c;适合快速构建传统的Web应用程序&#xff0c;其…

Qt加载SVG矢量图片,放大缩小图片质量不发生变化。

前言&#xff1a; 首先简单描述下SVG: SVG 意为可缩放矢量图形&#xff08;Scalable Vector Graphics&#xff09;。 SVG 使用 XML 格式定义图像。 给界面或者按钮上显示一个图标或背景图片&#xff0c;日常使用.png格式的文件完全够用&#xff0c;但是有些使用场景需要把图…

QChartView显示实时更新的温度曲线图(二)

文章目录 参考图说明1. 项目结构2. TempChartView.pro3. main.cpp4. TemperatureSeries.qml5. main.qml详细说明 参考图 说明 Qt Charts 提供了一系列使用图表功能的简单方法。它使用Qt Graphics View Framework 图形视图框架&#xff0c;因此可以很容易集成到用户界面。可以使…

基于小波分析的纹理和颜色反射对称性检测(MATLAB R2018A)

对称物体在自然图像和合成图像中普遍存在。作为对称物体最重要的全局特征之一&#xff0c;对称性检测长期以来都是计算机视觉领域的研究热点&#xff0c;并在图片的语义提取、图像语义理解以及情感识别等任务上具有广泛的应用。对称物体的检测技术&#xff0c;就是将图片中所蕴…

【前端】HTML+CSS复习记录【3】

文章目录 前言一、from&#xff08;表单&#xff09;二、style属性1、标签中直接定义&#xff08;内联样式&#xff09;2、定义在head中3、外部链接引用 四、 class 选择器系列文章目录 前言 长时间未使用HTML编程&#xff0c;前端知识感觉忘得差不多了。通过梳理知识点&#…

qq文件传输助手在哪里?详细图文教程告诉你(2024新版)

QQ作为一款功能强大的社交软件&#xff0c;不仅提供了聊天、语音、视频等多种通讯方式&#xff0c;还内置了文件传输助手这一实用工具。通过文件传输助手&#xff0c;用户可以在不同设备之间轻松传输文件&#xff0c;实现跨平台的便捷操作。 那么&#xff0c;qq文件传输助手在…

【@AutoWired和@Resource的区别】

AutoWired和Resource的区别 这两个我们在项目中&#xff0c;经常去使用。很少有人知道他们有什么区别。下面我们将从 来源依赖查找顺序支持的参数依赖注入的用法支持 这四个方面来说明他们俩个的区别 来源 Autowired: 这是Spring框架自带的注解&#xff0c;用于实现自动依…