LC-单词拆分、最长递增子序列、乘积最大子数组、分割等和子集、最长有效括号

devtools/2025/2/28 21:58:29/

单词拆分

  1. 定义状态

    • dp[i] 表示字符串 s 的前 i 个字符 s[0:i] 是否可以由 wordDict 中的单词拼接而成。
  2. 状态转移方程

    • wordDict 中的某个单词 word,如果 s[j:i] (即 sji-1 的子串) 在 wordDict 中,且 dp[j] == true,则 dp[i] = true。 dp[i]=dp[j]且s[j:i]∈wordDictdp[i] = dp[j] \quad \text{且} \quad s[j:i] \in wordDictdp[i]=dp[j]且s[j:i]∈wordDict
    • 其中 j 取值范围为 [0, i)
  3. 初始化

    • dp[0] = true,表示空字符串可以被成功分解(作为起点)。
    • 其他 dp[i] 初始为 false,表示尚未判断。
  4. 目标

    • 计算 dp[s.length()] 是否为 true,即 s 是否可以由 wordDict 拼接而成。
class Solution {public boolean wordBreak(String s, List<String> wordDict) {Set<String> wordSet = new HashSet<>(wordDict);//使用哈希集合存储字典int n = s.length();boolean[] dp = new boolean[n + 1];dp[0] = true;//空字符串可以被成功分解for(int i = 1;i <= n;i++){//遍历可能的拆分点for(int j = 0;j < i;j++){if(dp[j] && wordSet.contains(s.substring(j,i))){dp[i] = true;break;//找到一个可行的拆分点就可以停止}}}return dp[n];}
}

最长递增子序列

动态规划

  • 使用动态规划的方法可以较为直观地解决这个问题。我们定义一个数组 dp,其中 dp[i] 表示以 nums[i] 为结尾的最长递增子序列的长度。
  • 状态转移方程:对于每个 nums[i],我们可以遍历所有之前的元素 nums[j],如果 nums[i] > nums[j],则 dp[i] = max(dp[i], dp[j] + 1)
  • 最终的答案就是 dp 数组中的最大值。

时间复杂度是 O(n^2),其中 n 是数组的长度。

class Solution {public int lengthOfLIS(int[] nums) {if(nums.length == 0){return 0;}int n = nums.length;int[] dp = new int[n];dp[0] = 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);}}}int max = 0;for(int i = 0;i < n;i++){max = Math.max(max,dp[i]);}return max;}
}

乘积最大子数组

  1. 动态规划:我们使用动态规划的方法来解决这个问题。由于数组中有负数,可能会影响最大值和最小值,所以我们需要追踪当前位置的最大值最小值

    • maxProduct[i] 表示到第 i 个元素为止的子数组的最大乘积。
    • minProduct[i] 表示到第 i 个元素为止的子数组的最小乘积。

    每次更新最大值和最小值时,都要考虑:

    • 当前元素本身 nums[i]
    • 当前元素和之前的最大值 maxProduct[i-1] 乘积
    • 当前元素和之前的最小值 minProduct[i-1] 乘积(负数乘积可能变大)
  2. 状态转移:对于每一个元素 nums[i],我们要更新当前的最大值和最小值:

    • maxProduct[i] = max(nums[i], nums[i] * maxProduct[i-1], nums[i] * minProduct[i-1])
    • minProduct[i] = min(nums[i], nums[i] * maxProduct[i-1], nums[i] * minProduct[i-1])
  3. 最终,我们需要返回所有 maxProduct 中的最大值。

class Solution {public int maxProduct(int[] nums) {if(nums == null || nums.length == 0){return 0;}//初始化int maxProd = nums[0];//记录当前的最大乘积int minProd = nums[0];//记录当前的最小乘积int result = nums[0];for(int i = 1;i < nums.length;i++){int tempMax = maxProd;maxProd = Math.max(nums[i],Math.max(nums[i] * maxProd,nums[i] * minProd));minProd = Math.min(nums[i],Math.min(nums[i] * tempMax,nums[i] * minProd));result = Math.max(result,maxProd);}return result;}
}

分割等和子集

  1. 问题转化

    • 如果可以将数组分割成两个子集,使得它们的元素和相等,那么这两个子集的和应该是 sum(nums) / 2
    • target = sum(nums) / 2,如果 sum(nums) 是奇数,那么直接返回 false,因为无法将奇数均分成两个整数。
    • 如果 sum(nums) 是偶数,问题就变成了:能否在数组中找到一个子集,它的和为 target
  2. 动态规划

    • 我们可以使用动态规划来判断是否能找到一个和为 target 的子集。可以使用一个布尔型数组 dp,其中 dp[i] 表示是否能从 nums 中找到一个子集,使得该子集的和为 i
    • 初始状态是 dp[0] = true,表示和为 0 的子集是存在的(即不选任何元素)。
    • 对于每个元素 num,我们从 target 开始倒着更新 dp[i],使得每次状态转移时不会覆盖前一步的结果。
class Solution {public boolean canPartition(int[] nums) {int sum = 0;for(int num : nums){sum += num;}//如果总和是奇数,不能分割成两个子集if(sum % 2 != 0){return false;}int target = sum / 2;boolean[] dp = new boolean[target + 1];dp[0] = true;//和为0的子集是空集for(int num : nums){//从大到小更新dp数组,确保不重复使用同一个元素for(int i = target;i >= num;i--){dp[i] = dp[i] || dp[i - num];}}return dp[target];}
}

最长有效括号

动态规划的方法通过定义一个状态数组 dp 来表示以当前位置为结尾的最长有效括号长度。

  1. 定义 dp[i] 为以 i 为结尾的最长有效括号的长度。
  2. 遍历字符串,对于每个 )
    • 如果 s[i]),则判断 s[i-1] 是否为 (,若是,则构成一个有效括号,dp[i] = dp[i-2] + 2
    • 如果 s[i-1]),则判断 s[i - dp[i-1] - 1] 是否为 (,如果是,则组成更长的有效括号,更新 dp[i]
  3. 最终返回 dp 数组中的最大值。
class Solution {public int longestValidParentheses(String s) {int n = s.length();if(n == 0){return 0;}int[] dp = new int[n];int maxLength = 0;for(int i = 1;i < n;i++){if(s.charAt(i) == ')'){if(s.charAt(i - 1) == '('){dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;}else if(i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '('){dp[i] = dp[i - 1] + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] -  2] : 0) + 2;}maxLength = Math.max(maxLength,dp[i]);}       }return maxLength;}
}

http://www.ppmy.cn/devtools/163454.html

相关文章

Geo3D城市引擎大规模建筑植被渲染

import * as Geo3D from "../src"; import InitHelper from "./InitHelper"; //3D场景初始化 const sceneControl InitHelper.init3D(); const container document.querySelector("#map") as HTMLElement; container && sceneControl.…

【Viewer.js】vue3封装图片查看器

效果图 需求 点击图片放大可关闭放大的 图片 下载 cnpm in viewerjs状态管理方法 stores/imgSeeStore.js import { defineStore } from pinia export const imgSeeStore defineStore(imgSeeStore, {state: () > ({showImgSee: false,ImgUrl: ,}),getters: {},actions: {…

Ubuntu 下通过 Docker 部署 Nginx 服务器

Docker 和 Nginx 简介 Docker 是一种开源平台&#xff0c;旨在简化应用程序的开发、交付和运行。通过容器化技术&#xff0c;Docker 能够将应用及其依赖项封装在一个独立的环境中&#xff0c;确保在任何地方都能一致地运行。Nginx 是一款高性能的 HTTP 和反向代理服务器&#…

C++ 常见面试知识点

主要介绍C常见面试题 1、说一下你理解的C中的四种智能指针 常用接口 T* get(); T& operator*(); T* operator->(); T& operator(const T& val); T* release(); 将 封装在内部的指针置为nullptr, 但并不会破坏指针所指向的内容, 函 数返回的是内部指针置空之前…

零基础学QT、C++(四)QT程序打包

项目包 链接&#xff1a;https://pan.quark.cn/s/6a3455161757 记得创建yolo的数据库&#xff0c;并把数据导入 目录 一、把项目变为release版 二、运行项目 三、找到windeployqt6.exe 四、运行exe 五、无法定位重新输入点 学习视频 QT程序打包发布教程&#xff01;&#xff01…

[Lc优选算法] 双指针 | 移动零 | 复写零 | 快乐数

目录 &#x1f3a2;1.移动零 题解 代码 ⭕2.复写零 题解 代码 ⭕3.快乐数 题解 代码 &#x1f3a2;1.移动零 题目链接&#xff1a;283. 移动零 题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零…

Python 爬虫实战案例 - 获取社交平台事件热度并进行影响分析

目录 一、引言 二、数据爬取 三、数据分析 四、可视化展示 五、总结 一、引言 在当今信息爆炸的时代&#xff0c;社交平台成为了各类事件发酵和传播的重要场所。了解社交平台上事件的热度以及其潜在影响&#xff0c;对于舆情监测、市场营销、社会趋势分析等领域具有重要意…

前端性能测试面试题及参考答案

目录 前端性能测试中,首屏时间(FCP)和白屏时间的定义及测量方法是什么? 解释浏览器渲染过程中关键路径(Critical Rendering Path)的组成部分。 如何通过 Navigation Timing API 统计页面加载各阶段耗时? 什么是 LCP(Largest Contentful Paint)?如何优化? 前端性…