1143. 最长公共子序列

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

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

例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例 1:

输入:text1 = "abcde", text2 = "ace"

输出:3

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

示例 2:

输入:text1 = "abc", text2 = "abc"

输出:3

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

示例 3:

输入:text1 = "abc", text2 = "def"

输出:0

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

提示:

1 <= text1.length <= 1000

1 <= text2.length <= 1000

输入的字符串只含有小写英文字符。

思路

//思路1:暴力,找出其中一个string的所有子序列,然后拿去第二个进行匹配,匹配到即为公告子序列
//思路2:动态规划,将两个String当初二维数组的行列,从两个String的第一个字符关系进行比较,逐渐增加字符个数,递推进行

solution1 dp

class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m+1][n+1];
for (int i=1; i<m+1; i++){
for (int j=1; j<n+1; j++){
if (text1.charAt(i-1) == text2.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1] + 1; //(i,j)=(i-1,j-1),因此从1开始遍历到m+1
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[m][n];
}
}

dp2

//通过将字符串转化为char数组提升性能
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] chars1 = text1.toCharArray();
char[] chars2 = text2.toCharArray();
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m+1][n+1];
for(int i = 1; i < m+1; i++){
for(int j = 1; j < n+1; j++){
if (chars1[i-1] == chars2[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];
}
}

最长公共子串

class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] chars1 = text1.toCharArray();
char[] chars2 = text2.toCharArray();
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m+1][n+1];
int max = 0;
for(int i = 1; i < m+1; i++){
for(int j = 1; j < n+1; j++){
if (chars1[i-1] == chars2[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
max = Math.max(max,dp[i][j]);
}
}
}
return max;
}
}

300. 最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

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

输出: 4

解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

说明:

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。

你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

solution1 普通动态规划

class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp,1);
for (int i = 0; i < nums.length; i++){
for (int j = 0; j < i; j++){
if(nums[j]<nums[i]) dp[i] = Math.max(dp[i],dp[j]+1);
}
}
int res = 0;
for(int i = 0; i < nums.length; i++){
res = Math.max(res,dp[i]);
}
return res;
}
}
//普通dp
//1.dp数组
//2.求base case
//2.数学归纳法求dp[i],即确定状态转移方程

solution2 优化dp

class Solution {
public int lengthOfLIS(int[] nums) {
int[] top = new int[nums.length];
int piles = 0;
for (int i = 0; i < nums.length; i++){
int left = 0, right = piles;
int curr = nums[i];
//二分查找,查找小于当前且最大的数
while(left < right){
int mid = (left + right) >> 1;
if (top[mid] > curr){
right = mid;
}else if(top[mid] < curr){
left = mid + 1;
}else{
right = mid;
}
}
//牌堆最大值小于当前,新建堆
if (left == piles) piles ++;
top[left] = curr;
}
return piles;
}
}
//优化dp
//在普通dp状态方程的求解上,利用二分查找,将时间复杂度缩小到 O(log N)
//参考资料:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-she-ji-fang-fa-zhi-pai-you-xi-jia/

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]

输出: 6

解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
for(int i = 1; i < nums.length; i++){
dp[i] = Math.max(nums[i],nums[i]+dp[i-1]);
}
int res = Integer.MIN_VALUE;
for(int j = 0; j < nums.length; j++){
res = Math.max(dp[j],res);
}
return res;
}
} //!!!考虑每一数加不加入之前还是另起数组 //1、dp数组,每一状态保存有当前数加入的最大和子串
//2、dp[0] = nums[0]
//3、状态转移:max(nums[i],nums[i]+dp[i-1])

72. 编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符

删除一个字符

替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"

输出:3

解释:

horse -> rorse (将 'h' 替换为 'r')

rorse -> rose (删除 'r')

rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"

输出:5

解释:

intention -> inention (删除 't')

inention -> enention (将 'i' 替换为 'e')

enention -> exention (将 'n' 替换为 'x')

exention -> exection (将 'n' 替换为 'c')

exection -> execution (插入 'u')

solution1 暴力递归

class Solution {
public int minDistance(String word1, String word2) {
char[] c1 = word1.toCharArray();
char[] c2 = word2.toCharArray();
return helper(c1,c2,c1.length-1,c2.length-1);
}
public int helper(char[] c1, char[] c2, int n, int m){
if (n == -1) return m+1;
if (m == -1) return n+1; if (c1[n] == c2[m]){
return helper(c1,c2,n-1,m-1); //相同不用改变
}else{ //删除、插入、交换改变最小的那一个
return Math.min(helper(c1,c2,n-1,m)+1,
Math.min(helper(c1,c2,n,m-1)+1,
helper(c1,c2,n-1,m-1)+1)
);
}
} }
// String st = String.valueOf(c);
// char[] c = st.toCharArray();
//类似最长公共子序列
//思路1:暴力递归 找出每个操作中删除、插入、交换改变最小的那一个

solution2 带备忘录的递归(自顶向下)

class Solution {
public int minDistance(String word1, String word2) {
char[] c1 = word1.toCharArray();
char[] c2 = word2.toCharArray();
int[][] menu = new int[c1.length][c2.length];
return helper(c1,c2,c1.length-1,c2.length-1,menu);
}
public int helper(char[] c1, char[] c2, int n, int m, int[][] menu){
if (n == -1) return m+1;
if (m == -1) return n+1;
if (menu[n][m] != 0) return menu[n][m]; if (c1[n] == c2[m]){
return menu[n][m] = helper(c1,c2,n-1,m-1,menu); //相同不用改变
}else{ //删除、插入、交换改变最小的那一个
return menu[n][m] = Math.min(helper(c1,c2,n-1,m,menu)+1,
Math.min(helper(c1,c2,n,m-1,menu)+1,
helper(c1,c2,n-1,m-1,menu)+1)
);
}
} }
// String st = String.valueOf(c);
// char[] c = st.toCharArray();
//类似最长公共子序列
//思路1:暴力递归 找出每个操作中删除、插入、交换改变最小的那一个
//思路2:加个备忘录进行记录

solution3 动态规划(自底向上)

class Solution {
public int minDistance(String word1, String word2) {
char[] c1 = word1.toCharArray();
char[] c2 = word2.toCharArray();
int[][] dp = new int[c1.length+1][c2.length+1];
//base case
for (int i = 0; i < c1.length+1; i++) dp[i][0] = i;
for (int j = 0; j < c2.length+1; j++) dp[0][j] = j;
//状态转移
for (int i = 1; i < c1.length+1; i++){
for (int j = 1; j < c2.length+1; j++){
if(c1[i-1] == c2[j-1]) dp[i][j] = dp[i-1][j-1];
else dp[i][j] = Math.min(dp[i-1][j]+1,Math.min(dp[i][j-1]+1,dp[i-1][j-1]+1));
}
}
return dp[c1.length][c2.length];
}
}
// String st = String.valueOf(c);
// char[] c = st.toCharArray();
//类似最长公共子序列
//思路1:暴力递归 找出每个操作中删除、插入、交换改变最小的那一个
//思路2:加个备忘录进行记录
//思路3:1.dp数组 2.base case 3.根据实际情况列动态递归方程

LeetCode DP篇-求子序列问题(1143、300、53、72)的更多相关文章

  1. 子序列 sub sequence问题,例:最长公共子序列,[LeetCode] Distinct Subsequences(求子序列个数)

    引言 子序列和子字符串或者连续子集的不同之处在于,子序列不需要是原序列上连续的值. 对于子序列的题目,大多数需要用到DP的思想,因此,状态转移是关键. 这里摘录两个常见子序列问题及其解法. 例题1, ...

  2. 区间DP+next求循环节 uva 6876

    // 区间DP+next求循环节 uva 6876 // 题意:化简字符串 并表示出来 // 思路:dp[i][j]表示 i到j的最小长度 // 分成两部分 再求一个循环节 #include < ...

  3. HDU 1087 简单dp,求递增子序列使和最大

    Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...

  4. 【dp】求最长上升子序列

    题目描述 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.我们想知道此时最长上升子序列长度是多少? 输入 第一行一个整数N,表示我们要将1到N插入序列中 ...

  5. [LeetCode]152. 乘积最大子序列(DP)

    题目 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6. 示 ...

  6. 【dp】求最长公共子序列

    [题目描述] 一个给定序列的子序列是在该序列中删去若干元素后得到的序列.确切地说,若给定序列X=<x1,x2,…,xm>X=<x1,x2,…,xm>,则另一序列Z=<z1 ...

  7. LeetCode——数组篇:659. 分割数组为连续子序列

    659. 分割数组为连续子序列 输入一个按升序排序的整数数组(可能包含重复数字),你需要将它们分割成几个子序列,其中每个子序列至少包含三个连续整数.返回你是否能做出这样的分割? 示例 1: 输入: [ ...

  8. Monkey and Banana(dp,求最长的下降子序列)

    A group of researchers are designing an experiment to test the IQ of a monkey. They will hang a bana ...

  9. [LeetCode] Wiggle Subsequence 摆动子序列

    A sequence of numbers is called a wiggle sequence if the differences between successive numbers stri ...

  10. HDU-3944 DP?(组合数求模)

    一.题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=3944 二.题意 给一个巨大的杨辉三角,采用类似DP入门题“数字三角形”的方式求从顶点$(0, 0) ...

随机推荐

  1. Go泛型解密:从基础到实战的全方位解析

    本篇文章深入探讨了Go语言的泛型特性,从其基础概念到高级用法,并通过实战示例展示了其在实际项目中的应用. 关注[TechLeadCloud],分享互联网架构.云服务技术的全维度知识.作者拥有10+年互 ...

  2. Kafka的零拷贝技术Zero-Copy

    传统的拷贝过程 流程步骤: (1)操作系统将数据从磁盘文件中读取到内核空间的页面缓存: (2)应用程序将数据从内核空间读入用户空间缓冲区: (3)应用程序将读到数据写回内核空间并放入socket缓冲区 ...

  3. React项目中webpack的配置过程

    初始化一个web项目 使用npm init -y 初始化一个项目 在项目目录下创建src, dist文件夹,创建webpack.config.js配置文件 然后在src文件夹下创建index.js, ...

  4. YbtOJ 质数与约数 4.统计元素

    搜不到题解,乐. 由题意,\(a_i\) 对答案有贡献当且仅当 \(i\in{[l,r]}\) 且对于 \(\forall a_j| {a_i},j\notin[l,r]\). 则对于每个 \(a_i ...

  5. 你还在为SFTP连接超时而困惑么?

    1. 前言 在最近的项目联调过程中,发现在连接上游侧SFTP时总是需要等待大约10s+的时间才会出现密码输入界面,这种长时间的等待直接导致的调用文件接口时连接sftp超时问题.于是决定自己针对该问题进 ...

  6. STL unordered类容器浅谈

    一个代码: #include<cstdio> #include<vector> #include<functional> #include<algorithm ...

  7. Vue + Element UI 实现复制当前行数据功能(复制到新增页面组件值不能更新等问题解决)

    1.需求 使用Vue + Element UI 实现在列表的操作栏新增一个复制按钮,复制当前行的数据可以打开新增弹窗后亦可以跳转到新增页面,本文实现为跳转到新增页面. 2.实现 1)列表页 index ...

  8. 在路上---学习篇(一)Python 数据结构和算法 (6)基于GA(遗传)算法的小案例

    独白 最近了解到一种算法叫遗传算法,对其比较感兴趣,研究了一下,是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法.遗传算法是从代表问题可 ...

  9. .NET8 依赖注入

    依赖注入(Dependency Injection,简称DI)是一种设计模式,用于解耦组件(服务)之间的依赖关系.它通过将依赖关系的创建和管理交给外部容器来实现,而不是在组件(服务)内部直接创建依赖对 ...

  10. vim的各种快捷键

    目录 一.准备一份用于练习的文件 二.命令模式下的快捷键 1.移动光标 2.删除文字 3.复制粘贴 4.撤销上一次操作 5.恢复上一次撤销的操作 6.查找 7.替换单个字符 8.光标移动到最左边 9. ...