算法题(动态规划)

一、题目

1、单词拆分(LC 139)

2、最长递增子序列(LC 300)

二、题解

1、单词拆分(LC 139)

(1)分析

这道题要求判断一个字符串能否按照字典里的单词进行拆分,本质是一个递归选择问题,可以用记忆化 DFS来做。

整体思路是从字符串的末尾往前拆分,每次从后往前截取一段单词,如果这段单词刚好在字典里,就继续递归判断前面剩下的字符串能不能继续拆分,直到把整个字符串拆完。为了提高查询速度,可以先把字典里的单词放进 HashSet,这样判断单词是否存在的速度会快很多,同时提前找出字典里最长单词的长度,用来限制截取范围,避免做无用的截取操作。

递归函数的核心是记录当前处理到字符串的哪个位置,用 i 表示当前处理到第 i 个字符,当 i 等于 0 时,说明整个字符串已经成功拆分完毕,直接返回成功。为了避免重复递归,使用一个 memo 记忆数组,记录每个位置能否成功拆分,如果已经计算过就直接返回结果,不用再重复执行递归。

在递归内部,从当前位置往前截取单词,长度不超过字典最长单词长度,只要找到一段存在于字典中的单词,并且前面的部分也能成功拆分,就说明当前位置是可以拆分的,记录结果并返回。如果遍历完所有可能都无法拆分,就记录为不可拆分。

(2)解答
java 复制代码
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int n = s.length();   //字符串的长度
        int maxLen = 0;       //字典字符串的最大长度
        for(String str : wordDict){
            maxLen = Math.max(maxLen, str.length());  //找出字典字符串的最大长度
        }
        Set<String> word =new HashSet<>(wordDict);  //用HashSet集合,提高查询效率
        int[] memo = new int[n + 1];       //记忆数组
        Arrays.fill(memo, -1);            //初始化元素为-1

        return dfs(n, maxLen, word, s, memo) == 1; 


    }
    public int dfs(int i, int maxLen, Set<String> word, String s, int[] memo){
        if( i == 0){ //成功拆分
            return 1;
        }
        if(memo[i] != -1){   //已经计算过
            return memo[i];
        }
        for(int j = i - 1; j >= Math.max(0, i - maxLen); j--){
            if(word.contains(s.substring(j, i)) && dfs(j, maxLen, word, s, memo) == 1){
                return memo[i] = 1;   //字典集合中存在当前字符串,且前面的字符串也能够成功拆分
            }
        }
        return memo[i] = 0;
    }
}

2、最长递增子序列(LC 300)

(1)分析

这道题求数组里最长的递增子序列长度,也可以用记忆化 DFS来实现。

核心思路是:以数组中每一个元素作为子序列的结尾,分别求出以它结尾的最长递增子序列长度,最后在所有结果里取最大值,就是整个数组的答案。递归函数的作用就是计算以第 i 个元素结尾的最长递增子序列长度,这样问题就被拆分成一个个更小的子问题。

在递归过程中,遍历 i 前面所有的元素,如果某个元素比 nums i 小,说明可以把 nums i 接在以这个元素结尾的子序列后面,形成更长的递增子序列。在所有符合条件的结果里选出最大的那一个,再加上当前元素本身,就是以 i 结尾的最长长度。为了避免重复计算,用 memo 数组记录已经算过的结果,只要某个位置算过,就直接返回,不用再重新递归。最后所得的 ans 值即为所求。

(2)解答
java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;    //数组长度
        int[] memo = new int[n];  //记忆数组,存放以每个元素结尾的最长子序列大小
        int ans = 0;
        for(int i = 0; i < n; i++){
            ans = Math.max(ans, dfs(i, memo, nums)); //dfs(i) 以nums[i]结尾的最长子序列的长度
        }
        return ans;
        
    }
    public int dfs(int i, int[] memo, int[] nums){
        if(memo[i] != 0 ){          //说明计算过了  
        return memo[i];
        }
        int res = 0;
        for(int j = 0; j < i; j++){
            if(nums[i] > nums[j]){          //找到j<i且 nums[j]<nums[i],取最大的存放在memo[i]
                res = Math.max(res, dfs(j, memo, nums));
            }
        }
        res++;        //比前面最大的多一个
        return memo[i] = res;
    }
}
相关推荐
旺仔仔仔18 分钟前
机器人项目实战(5):从单次抓取,到接收指令连续做事
算法
旺仔仔仔26 分钟前
机器人项目实战(2):识别物体、提取点云,再到抓取姿态
算法
姜鱼问生28 分钟前
密码明明一样,为什么 GPG 说“错的“?
后端·算法·架构
启观川43 分钟前
数据结构与算法-第 3 章 常用算法-贪心算法
数据结构·笔记·python·算法·贪心算法
对空六课1 小时前
指标口径生效时间的版本查询实现
大数据·服务器·前端·算法·数据分析
对空六课1 小时前
RFM分层边界变化的回溯计算
大数据·服务器·前端·数据库·人工智能·算法·数据分析
一勺思维1 小时前
我为什么把 Agent 的核心逻辑从 LangChain 里搬出来手写
算法
碧海银沙音频科技研究院1 小时前
基于炬芯ATS308X的LE aduio LC3编解码算法功能实现
算法
小蒋观天下1 小时前
厂区人员徘徊游荡判别——基于时序轨迹速度的逗留行为分级算法落地
大数据·人工智能·深度学习·算法·机器学习·ai大模型
程序员清风2 小时前
堆与优先队列:从 Top K 到任务调度
java·数据结构·算法