《dp补卡——子序列问题》

目录

  • 300. 最长递增子序列
  • 674. 最长连续递增序列
  • 718. 最长重复子数组
  • 1143. 最长公共子序列
  • 53. 最大子序和
  • 392. 判断子序列
  • 115. 不同的子序列
  • 583. 两个字符串的删除操作
  • 72. 编辑距离
  • 647. 回文子串 (与 5.最长回文子串思路差不多)
  • 516. 最长回文子序列

300. 最长递增子序列

step1:dp[i]:下标<=i的最长子序列长度。即以 nums[i] 结尾 的「上升子序列」的长度,注意以nums[nums.size()-1]结尾的上升子序列长度并不一定是全局最优值,所以要从dp[i]中找到最大值。
step2:状态转移方程:

if(nums[i] > nums[j]) dp[i] = max(dp[i],dp[j]+1);	//取得dp[j]+1中最大的那个值作为dp[i]

step3:初始化
每一个i,至少都是1.
step4:遍历顺序,从前向后遍历。

class Solution {
public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();vector<int> dp(n,1);int result = 1;for(int i = 0; i < n; i++){int j_maxlen = 1;for(int j = 0; j < i; j++){if(nums[i] > nums[j])j_maxlen = max(j_maxlen,dp[j]+1);}dp[i] = j_maxlen;if(dp[i] > result) result = dp[i];}return result;}
};

674. 最长连续递增序列

简单题,迅速AC。不过这个是贪心的思路。

class Solution {
public:int findLengthOfLCIS(vector<int>& nums) {int n = nums.size();if(n == 0) return 0;int len=1;int maxlen =1;for(int i = 1; i < n; i++){if(nums[i] > nums[i-1]){len+=1;}elselen = 1;maxlen = max(maxlen,len);}return maxlen;}
};

下面使用dp思路:
step1:dp[i]以下标为i结尾的数组连续递增的子序列长度为dp[i]
step2: 如果nums[i+1] > nums[i],那么以i+1结尾的连续递增的子序列长度一定等于以i结尾的数组的连续递增的子序列长度+1
dp[i+1] = dp[i] + 1
否则,dp不进行赋值,同时需要注意以nums.size()-1结尾的连续递增子序列长度不一定为全局最优值。

class Solution {
public:int findLengthOfLCIS(vector<int>& nums) {int n = nums.size();if(n == 0) return 0;vector<int> dp(n,1);int result = 1;for(int i = 1; i < n; i++){if(nums[i] > nums[i-1]){dp[i] = dp[i-1] + 1;}result = max(result,dp[i]);}return result;}
};

718. 最长重复子数组

在这里插入图片描述

class Solution {
public:int findLength(vector<int>& nums1, vector<int>& nums2) {int len1 = nums1.size();int len2 = nums2.size();vector<vector<int>> dp(len1,vector<int>(len2,0));int result = 0;for(int i = 0; i < len1; i++){for(int j = 0; j < len2; j++){if(nums1[i] == nums2[j]){if(i == 0 || j == 0){dp[i][j] = 1;}elsedp[i][j] = dp[i-1][j-1]+1;}result = max(result,dp[i][j]);}}return result;}
};

1143. 最长公共子序列

这里不要求子序列是连续的,但是要相对有顺序。
step1:
dp[i][j]:长度为[0,i]的字符串text1与长度为[0,j]的字符串text2的最长公共子序列长度
step2:

if(text1[i] == text[j]) 
{//找到了一个公共元素dp[i][j] = dp[i-1][j-1]+1;
}
else
{//没有找到公共元素,就选择dp[i-1][j]与dp[i][j-1]中最大的dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}

在这里插入图片描述
step3:初始化比较麻烦。

for(int i = 0; i < len1; i++)
{if( (i >= 1 && dp[i-1][0] == 1 ) || (text1[i] == text2[0]) ) dp[i][0] = 1;
}
for(int j = 0; j < len2; j++)
{if( (j >= 1 && dp[0][j-1] == 1 ) || (text2[j] == text1[0]) ) dp[0][j] = 1;
}

AC代码:

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int len1 = text1.size();int len2 = text2.size();vector<vector<int>> dp(len1,vector<int>(len2,0));int maxlen = 0;//初始化for(int i = 0; i < len1; i++){if( (i >= 1 && dp[i-1][0] == 1 ) || (text1[i] == text2[0]) ) dp[i][0] = 1;}for(int j = 0; j < len2; j++){if( (j >= 1 && dp[0][j-1] == 1 ) || (text2[j] == text1[0]) ) dp[0][j] = 1;}//状态转移for(int i = 1; i < len1; i++){for(int j = 1; j < len2; j++){if(text1[i] == text2[j])dp[i][j] = dp[i-1][j-1]+1;else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);}}return dp[len1-1][len2-1];}
};

53. 最大子序和

step1:
dp[i],下标<=i的最大和。
step2:

if(dp[i-1] >= 0) dp[i] = dp[i-1] + nums[i];
else dp[i] = nums[i];

step3:
dp[0] = nums[0]
AC代码:

class Solution {
public:int maxSubArray(vector<int>& nums) {vector<int> dp(nums.size(),0);dp[0] = nums[0];int maxsum = dp[0];for(int i = 1; i < nums.size(); i++){if(dp[i-1] >= 0) dp[i] = dp[i-1] + nums[i];else dp[i] = nums[i];maxsum = max(maxsum,dp[i]);}return maxsum;}
};

392. 判断子序列

这一题与1143. 最长公共子序列基本是一个题目,不过需要加入一些边界输入判断,如果最长公共子序列长度等于短序列的长度就认为是子序列。
当然更加精准来说,两者的状态转移方程不同。
if (s[i] != t[j]), 说明此时长序列t要删除元素,把t[j]删除,则dp[i][j]则是s[i]与t[j-1]相比较了。
if(s[i] == t[j]),说明此时结果为dp[i][j]+1
至于为什么if (s[i] != t[j]),不能是从dp[i-1][j]推导得到,这是因为,dp[i-1][j]相当于是短序列删除元素s[i]从而与长序列保持一致。而题目中短序列是不能删除元素的。

图1 最长公共子序列
图2 判断子序列
class Solution {
public:bool isSubsequence(string text1, string text2) {int len1 = text1.size();int len2 = text2.size();if(len1 == 0) return true;if(len2 < len1) return false;vector<vector<int>> dp(len1,vector<int>(len2,0));int maxlen = 0;//初始化for(int i = 0; i < len1; i++){if( (i >= 1 && dp[i-1][0] == 1 ) || (text1[i] == text2[0]) ) dp[i][0] = 1;}for(int j = 0; j < len2; j++){if( (j >= 1 && dp[0][j-1] == 1 ) || (text2[j] == text1[0]) ) dp[0][j] = 1;}//状态转移for(int i = 1; i < len1; i++){for(int j = 1; j < len2; j++){if(text1[i] == text2[j])dp[i][j] = dp[i-1][j-1]+1;else dp[i][j] = dp[i][j-1];}}return dp[len1-1][len2-1] == len1;}
};

115. 不同的子序列

step1dp[i][j]:以i为结尾的s序列中出现以j结尾的t子序列的个数。
step2:
分析两种情况:
case1:s[i] 等于 t[j]
此时dp[i][j]由两部分组成:
一部分是用s[i]匹配,则dp[i][j] = dp[i-1][j-1]
一部分不用s[i]匹配,则dp[i][j] = dp[i-1][j]
如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。
所以:

dp[i][j] = dp[i-1][j-1] + dp[i-1][j];

case2:s[i] 不等于 t[j]
此时,dp[i][j]只能由dp[i-1][j]推导来
即:dp[i][j] = dp[i-1][j]

step3:
我们已经直到状态由dp[i-1][j] 或者 dp[i-1][j-1]
所以dp[i][0] 和 dp[0][j]都一定要初始化。
dp[i][0]:代表以下标i结尾的s序列出现以下标0为结尾的子序列个数。

int times = 0;
for(int i = 0; i < len1; i++)
{if(s[i] == t[0]) times++;dp[i][0] = times;
}

dp[0][j]: 代表以下标0结尾的s序列出现以下标j为结尾的子序列个数。

if(s[0] == t[0]) dp[0][0] = 1;

其他情况均为0.

需要注意的地方:由于dp内存的数目过大,需要用long long
,并且对与求和的结果,需要对INT_MAX取模不然会溢出.

class Solution {
public:int numDistinct(string s, string t) {int len1 = s.size();int len2 = t.size();if(len2 > len1) return 0;vector<vector<long long>> dp(len1,vector<long long>(len2,0));//初始化int times = 0;for(int i = 0; i < len1; i++){if(s[i] == t[0]) times++;dp[i][0] = times;}//递推for(int i = 1; i < len1; i++){for(int j = 1; j < len2; j++){if(s[i] == t[j])dp[i][j] = (dp[i-1][j-1] + dp[i-1][j])%INT_MAX;elsedp[i][j] = dp[i-1][j];}}return dp[len1-1][len2-1];}
};

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

这一题,和上一题类似,不过状态转移的时候需要多考虑一点点。
还有就是dp数组定义与上面有点区别,这样对于初始化的时候稍微简便了。
step1:dp[i][j]:以i-1为结尾的字符串word1与以j-1为结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
step2:
分为两个情况:
case1:word1[i-1]与word2[j-1]相同的时候
此时dp[i][j] = dp[i-1][j-1]
case2:word1[i-1]与word2[j-1]相同的时候
此时存在三个子情况:

  • case1:删除word1[i-1]后,有相等机会,此时dp[i][j] = dp[i-1][j] + 1
  • case2:删除word2[j-1]后,有相等机会,此时dp[i][j] = dp[i][j-1] + 1
  • case3:同时删除word1[i-1]与word2[j-1],有相等机会,此时dp[i][j] = dp[i-1][j-1] + 2
    所以此时有
dp[i][j] = min{dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+2};

step3:
关于初始化,分为dp[i][0]dp[0][j]
dp[i][0]:word2为空字符串,以i-1结尾的字符串word1要删除i个元素,才能与word2相同。所以
dp[i][0] = i
dp[0][j]:word1为空字符串,以j-1结尾的字符串word2要删除j个元素,才能与word1相同。所以
dp[0][j] = j

AC代码:

class Solution {
public:int minDistance(string word1, string word2) {int len1 = word1.size();int len2 = word2.size();vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));//初始化for(int i = 0; i <= len1; i++)dp[i][0] = i;for(int j = 0; j <= len2; j++)dp[0][j] = j;for(int i = 1; i <= len1; i++){for(int j = 1; j <= len2; j++){if(word1[i-1] == word2[j-1])dp[i][j] = dp[i-1][j-1];elsedp[i][j] =min(dp[i-1][j-1]+2 , min(dp[i-1][j],dp[i][j-1]) + 1);}}return dp[len1][len2];}
};

72. 编辑距离

step1:dp[i][j]表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]
step2:递推公式
仍然分为两种情况:
word1[i-1] == word2[j-1],此时不需要操作,dp[i][j] = dp[i-1][j-1]
word1[i-1] != word2[j-1],此时进行增、删、换

  • case1:word1增加一个元素,使得word1[i-1]与word2[j-1]相同,此时dp[i][j] = dp[i-1][j]+1
  • case2:word1删除一个元素(等同于word2增加一个元素),使得word1[i-1]与word2[j-1]相同,此时dp[i][j] = dp[i][j-1]+1
  • case3:替换元素,word1替换word1[i-1],使得word1[i-1]与word2[j-1]相同,此时dp[i][j] = dp[i-1][j-1]+1
    所以递推公式如下:
if(word1[i-1] == word2[j-1])dp[i][j] = dp[i-1][j-1];
elsedp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;

step3:初始化
需要初始化dp[i][0] 和 dp[0][j]
dp[i][0]:下标为i-1结尾的字符串word1和空字符串word2的最近距离,此时dp[i][0] = i
同理dp[0][j] =j

class Solution {
public:int minDistance(string word1, string word2) {int len1 = word1.size();int len2 = word2.size();vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));//初始化for(int i = 0; i <= len1; i++)dp[i][0] = i;for(int j = 0; j <= len2; j++)dp[0][j] = j;//开始递推for(int i = 1; i <= len1; i++){for(int j = 1; j <= len2; j++){if(word1[i-1] == word2[j-1])dp[i][j] = dp[i-1][j-1];else dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1])) + 1;}}return dp[len1][len2];}
};

647. 回文子串 (与 5.最长回文子串思路差不多)

step1:dp[i][j],下标在[i,j]之间的字符串中是否为回文子串
step2:递推公式
if(s[i] == s[j])

  • case1 :下标i==j,说明是同一个字符,为true
  • case2 :下标i与j相差等于1,为true
  • case3 :下标i与j相差大于1,看[i+1,j-1]区间是否为true
if(s[i] == s[j])
{if( (j-i <= 1) || dp[i+1][j-1] == true ){result++;dp[i][j] = true;}
}

if(s[i] != s[j]) dp[i][j] = false
step3:初始化
dp[i][j]初始化为false
step4:遍历顺序
在递推公式可以看出,case3是根据dp[i+1][j-1]推导出来的。所以不能从上到小、从左到右遍历。
从下到上,从左到右遍历,保证dp[i+1][j-1]dp[i][j]之前运算。

for(int i = s.size() - 1; i >= 0; i--)
{for(int j = i; j < s.size(); j++){  }
}

注意,回顾dp[i][j]数组定义:下标在[i,j]之间的字符串中是否为回文子串,所以j必定>=i。
AC代码:

class Solution {
public:int countSubstrings(string s) {int len = s.size();vector<vector<bool>> dp(len,vector<bool>(len,false));int result = 0;for(int i = len - 1; i >= 0; i--){for(int j = i; j < len; j++){if(s[i] == s[j]){if( j - i <= 1 || dp[i+1][j-1] == true){result++;dp[i][j] = true;}}}}return result;}
};

516. 最长回文子序列

step1:dp[i][j]:下标在[i,j]内的最长回文子序列的长度为dp[i][j]
step2:
如果s[i] == s[j]那么dp[i][j] = dp[i+1][j-1] + 2;
如果s[i] != s[j],说明s[i] 和 s[j]的同时加入并不能增加[i,j]区间的长度,那么分别加入s[i]、s[j]看看哪个可以组成最长回文子序列。
加入s[j]的回文子序列长度为dp[i+1][j]
加入s[i]的回文子序列长度为dp[i]][j-1]
dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
step3:初始化
初始为0。当i==j时,该字符串为单个字符,回文子序列长度为1,即dp[i][j] = 1
step4:遍历顺序
依靠dp[i+1][j-1]、dp[i+1][j]、dp[i][j-1],所以遍历i从下到上。j从左到右。
根据dp数组定义:下标在[i,j]内的最长回文子序列的长度为dp[i][j]
所以j>=i。

class Solution {
public:int longestPalindromeSubseq(string s) {int len = s.size();vector<vector<int>> dp(len,vector<int>(len,0));for(int i = 0; i < len; i++) dp[i][i] = 1;for(int i = len - 1; i >= 0; i--){for(int j = i + 1; j < len; j++){if(s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2;else dp[i][j] = max(dp[i+1][j],dp[i][j-1]);}}return dp[0][len-1];}
};

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

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

相关文章

[LeetCode] Maximal Rectangle

Given a 2D binary matrix filled with 0s and 1s, find the largest rectangle containing all ones and return its area. 在做 Largest Rectangle in Histogram的时候有人说可以用在这题&#xff0c;看了一下还真是&#xff0c;以每行为x轴&#xff0c;每列往上累计的连续的…

什么是alpha测试_什么是ALPHA?

什么是alpha测试Α (ALPHA) Alpha is the first and foremost letter of the Greek alphabet. In the classification of Greek numerals or numbers, it constitutes a value of 1. Alpha是希腊字母的第一个也是最重要的字母 。 在希腊数字或希腊数字的分类中&#xff0c;它的…

《leetcode : 647. 回文子串 思考分析双指针解法》

647. 回文子串 如何确定是回文串&#xff1a; 找中心然后往两边扩散&#xff0c;判断是否对称即可。 在遍历中心点的时候&#xff0c;注意中心点可以是一个元素也可以是两个元素。 class Solution { public:int cal_two_extend(const string& s,int i,int j,int n){int re…

天草初级班(3)

算术运算指令算术运算指令是反映CPU计算能力的一组指令&#xff0c;也是编程时经常使用的一组指令。它包括&#xff1a;加、减、乘、除及其相关的辅助指令。 该组指令的操作数可以是8位、16位和32位(80386)。当存储单元是该类指令的操作数时&#xff0c;该操作数的寻址方式可以…

4.3.3版本之引擎bug

bug描述&#xff1a;   IOS设备上&#xff0c;当使用WWW www WWW.LoadFromCacheOrDownload(url, verNum); 下载资源时&#xff0c;第一次下载某个资源&#xff0c;www.assetBundle必定为空。 解决办法&#xff1a;   引擎版本降到4.3.2或者升到4.3.4或更高。 这个bug绝对是…

sml完整形式_411的完整形式是什么?

sml完整形式411&#xff1a;信息 (411: Information) 411 is an abbreviation of “Information". 411是“信息”的缩写 。 It is an expression, which is commonly used in messaging or chatting on social media networking sites like Facebook, Yahoo Messenger, a…

php 检测用户是否关闭浏览器

1、例子1 echo str_repeat(" ",3000);ignore_user_abort(true); mylog(online);while (true) {/** 1、程序正常结束 connection_status 0* 2、点击浏览器“停止”按钮 connection_status 1* 3、超时 connection_status 2*/echo "test<br>\n&qu…

explain用法

explain用法 EXPLAIN SELECT …… 变体&#xff1a; 1. EXPLAIN EXTENDED SELECT …… 将执行计划“反编译”成SELECT语句&#xff0c;运行SHOW WARNINGS 可得到被MySQL优化器优化后的查询语句 2. EXPLAIN PARTITIONS SELECT …… 用于分区表的EXPLAIN 执行计划包含的信息 id…

《位运算技巧以及Leetcode的一些位运算题目》

目录技巧练习位运算[461. 汉明距离](https://leetcode-cn.com/problems/hamming-distance/)[190. 颠倒二进制位](https://leetcode-cn.com/problems/reverse-bits/)[136. 只出现一次的数字](https://leetcode-cn.com/problems/single-number/)[260. 只出现一次的数字 III](http…

linux读取配置文件(C语言版)

一个通用的linux系统中C语言版读取配置文件的函数。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <errno.h>#define KEYVALLEN 100/* 删除左边的空格 */ char * l_trim(char * szOutput, con…

java 范围搜寻要怎么弄_搜索范围

java 范围搜寻要怎么弄Problem statement: 问题陈述&#xff1a; Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value. 给定一个以升序排列的整数nums数组&#xff0c;请找到给定目标值的开始和结束…

boa + ajax + cgi ajax请求cgi

最近公司要做一个通讯管理机,然后需要和另外一个同事一起做,我们需要用到boaAjaxCGI,以前没试过与CGI交互,一开始发现问题挺大的,用ajax请求cgi,总是不返回数据,又或者请求回来的是cgi的源码,后来发现,通过本地IIS或者直接打开html页面请求的,返回来的都是cgi的源码或者返回失败…

《DBNotes:single_table访问方法、MRR多范围读取优化、索引合并》

目录single_table访问方法constrefref_or_nullrangeindexallMRR多范围读取优化索引合并intersectionunionsort-unionsingle_table访问方法 const 在主键列或者unique二级索引与一个常数进行等值比较时才有效。 如果主键或者unique二级索引的索引列由多个列构成&#xff0c;则…

怎样通过命令管理Windows7桌面防火墙

&#xff08;1&#xff09;启用桌面防火墙netsh advfirewall set allprofiles state on&#xff08;2&#xff09;设置默认输入和输出策略netsh advfirewall set allprofiles firewallpolicy allowinbound,allowoutbound以上是设置为允许&#xff0c;如果设置为拒绝使用blockin…

ruby推送示例_Ruby for循环示例

ruby推送示例for循环 (The for loop) In programming, for loop is a kind of iteration statement which allows the block to be iterated repeatedly as long as the specified condition is not met or a specific number of times that the programmer knows beforehand. …

《DBNotes: Buffer Pool对于缓冲页的链表式管理》

目录Buffer Pool回顾Buffer Pool内部组成freelistflushlistLRU链表管理以及改进Buffer Pool回顾 我们知道针对数据库的增删改删操作都是在Buffer Pool中完成的&#xff0c;一条sql的执行步骤可以认为是这样的&#xff1a; 1、innodb存储引擎首先在缓冲池中查询有没有对应的数据…

一个延时调用问题

如果用下面第1行的写法&#xff0c;调用 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector(removeFromSuperview) object:nil]; 可以生效 如果用下面第3行的写法&#xff0c;调用 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:…

onclicklistener 方法使用汇总

相信很多像我一样的新手学习ANDROID开发会遇到这个问题&#xff0c;通过这几天的归类和总结&#xff0c;将我的理解写在下面&#xff0c;欢迎大家一起前来讨论&#xff1a; 以按钮BUTTON的监听事件为例&#xff0c;以下的监听实现都是等价的&#xff1a; 1.使用接口继承按钮监听…

《源码分析转载收藏向—数据库内核月报》

月报原地址&#xff1a; 数据库内核月报 现在记录一下&#xff0c;我可能需要参考的几篇文章吧&#xff0c;不然以后还得找&#xff1a; MySQL 代码阅读 MYSQL开源软件源码阅读小技巧 MySQL 源码分析 聚合函数&#xff08;Aggregate Function&#xff09;的实现过程 MySQL …

vim中的jk为什么是上下_JK的完整形式是什么?

vim中的jk为什么是上下JK&#xff1a;开玩笑 (JK: Just Kidding) JK is an abbreviation of "Just Kidding". JK是“ Just Kidding”的缩写 。 It is an expression, which is commonly used in messaging or chatting on social media networking sites like Faceb…