LeetCode刷题——动态规划打卡

本文深入讲解动态规划的主要思想,解题步骤及算法应用,通过四个LeetCode经典题目:最长连续递增序列、最长回文子串、最长回文子序列和编辑距离,详细解析动态规划在解决实际问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态规划

主要思想

若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。

动态规划法仅仅解决每个子问题一次,具有天然剪枝的功能,从而减少计算量,

一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。

解题步骤

  • 确定动态规划状态
  • 写出状态转移方程(画出状态转移表)
  • 考虑初始化条件
  • 考虑输出状态
  • 考虑对时间,空间复杂度的优化(Bonus)

算法应用

1、leetcode674.最长连续递增序列

题目描述:

给定一个未经排序的整数数组,找到最长且连续的的递增序列,并返回该序列的长度。

示例 1:

输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。 

示例 2:

输入: [2,2,2,2,2]
输出: 1
解释: 最长连续递增序列是 [2], 长度为1。

注意:数组长度不会超过10000。

程序代码(Java版)

class Solution {
	public static int findLengthOfLCIS(int[] nums) {
        if(nums.length == 0) return 0;
        int [] dp = new int[nums.length];
        dp[0] = 1;
        int res = dp[0];
        for(int i=1; i<nums.length; i++){
            if(nums[i] > nums[i-1]){
                dp[i] = dp[i-1] + 1;             
            }else {
            	dp[i] = 1;
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}
2、leetcode5. 最长回文子串

题目描述:

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

解题思路

首先定义字符串 s 从下标 i 到下标 j 的字串为 P(i, j),若 s 从下标 i 到下标 j 的字串为回文串,则 P(i, j)=true,否则 P(i, j) = false;
当字符串长度小于2时,肯定是回文;
想知道 P(i,j)的情况,只需要知道 P(i+1,j−1)的情况,再结合s[i] == s[j]进行判断;
当有回文时,就需要判断最长的回文了,比较每次得到的回文长度,赋值得到最大的回文和起始的索引。

程序代码(Java版)

class Solution {
    	public String longestPalindrome(String s) {
            int n = s.length();
            if(n < 2) return s;
            int max_len = 1;
            int start = 0;
            boolean dp[][]=new boolean[n][n];
            for(int i=0; i<n; i++){
                dp[i][i] = true;
            }
            for(int j=1; j<n; j++){
                for(int i=0; i<j; i++){
                    if(s.charAt(i) == s.charAt(j)){
                        if(j-i<3){
                            dp[i][j] = true;
                        }else{
                            dp[i][j] = dp[i+1][j-1];
                        }
                    }else{
                        dp[i][j] = false;
                    }
                    if(dp[i][j]){
                        int length = j-i+1;
                        if(length > max_len){
                            max_len = length;
                            start = i;
                        }
                    }
                }
            }
        return s.substring(start, start+max_len);
    }
}
3、leetcode516. 最长回文子序列

题目描述:

给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。

示例 1:

输入:
"bbbab"
输出:
4
一个可能的最长回文子序列为 "bbbb"

示例 2:

输入:
"cbbd"
输出:
2
一个可能的最长回文子序列为 "bb"

解题思路:
初始状态确定:dp[i][j]表示字符串s最长回文子序列的长度,子问题即是求每个回文子字符串的长度。

状态转移方程:当s[i] = s[j]时,就说明在原先的基础上又增加了回文子序列的长度,dp[i][j] = dp[i+1][j-1] + 2;当s[i]不等于 s[j]时,就说明s[i]、s[j]至少有一个不在回文子序列中,即我们只需要取s[i-1,j-1]加上s[i]或者s[j]的数值中较大的,即dp[i][j] = max(dp[i][j-1],dp[i+1][j]);

由题意可知:一个字符就能构成一个回文子序列,且长度为1,故dp[i][j] = 1,此时i一直是小于j的,不存在i大于j的情况。所以我们的dp表如下表:在这里插入图片描述
由上可知,我们需要从下往上的遍历顺序。
程序代码(Java版)

class Solution {
    public int longestPalindromeSubseq(String s) {
        int n = s.length();
        if(n == 0) return 0;
        int [][] dp = new int[n][n];
        for(int i=0; i<n; i++){
            dp[i][i] = 1;
        }
        //从下向上遍历
        for(int i=n-1; i>=0; i--){
            for(int j=i+1; j<n; j++){
                if(s.charAt(i) == s.charAt(j)){
                    dp[i][j] = dp[i+1][j-1] + 2;
                }else{
                    dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
                }
            }
        }
        //返回右上角位置的状态就是最大长度
        return dp[0][n-1];
    }
}
4、leetcode72.编辑距离

题目描述:
给你两个单词 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')

程序代码(Java版)

class Solution {
    public int minDistance(String word1, String word2) {
        int row = word1.length();
        int col = word2.length();
        
        //dp[i][j]表示word1[0...i]变为word2[0...j]所使用的最少操作数
        int[][] dp = new int[row+1][col+1];
        
        for(int j = 0; j <= col; j++){
            dp[0][j] = j; //插入字符
        }
        
        for(int i = 0; i <= row; i++){
            dp[i][0] = i;  //删除字符
        }
        
        for(int i = 1; i <= row ; i++){
            for(int j = 1; j <= col; j++){
                //末尾字符相同,不需要编辑.(对应字符相等,不操作.)(注意下标,dp[][]下标为从1开始,字符串下标从0开始)
                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-1][j], dp[i][j-1]));
                }               
            }
        }
        return dp[row][col];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值