当前位置: 首页 > news >正文

每日算法-250529

2909. 元素和最小的山形三元组 II

题目
题目截图1

思路

数组, 前后缀分解

解题过程

对于寻找满足特定条件的三元组 (nums[i], nums[j], nums[k])i < j < k 的问题,一个常见的思路是枚举中间元素 nums[j]

  1. 确定目标:我们要找的是和最小的 “山形三元组”,即 nums[i] < nums[j]nums[k] < nums[j]
  2. 枚举 nums[j]:当 j1 遍历到 n-2 (其中 n 是数组长度),我们需要快速找到 nums[j] 左侧的最小值 (prevMin) 和右侧的最小值 (suffixMinVal)。
  3. 左侧最小值:对于 nums[j] 左侧 [0, j-1] 区间的最小值,我们可以在遍历 nums[j] 的同时,用一个变量 prevMin 动态维护 nums[0...j-1] 的最小值。
  4. 右侧最小值:对于 nums[j] 右侧 [j+1, n-1] 区间的最小值,我们可以预处理一个后缀最小值数组 suffixMinsuffixMin[x] 表示 nums[x...n-1] 区间的最小值。这样,对于当前的 nums[j], 右侧的最小值就是 suffixMin[j+1]
  5. 判断与更新:当确定了 prevMinsuffixMin[j+1] 后,如果 prevMin < nums[j]nums[j] > suffixMin[j+1],说明找到了一个山形三元组。我们计算其和 prevMin + nums[j] + suffixMin[j+1],并用它来更新全局的最小和结果 ret
  6. 初始化与边界ret 初始化为 Integer.MAX_VALUE。如果遍历结束后 ret 仍为此初始值,说明不存在山形三元组,返回 -1。

复杂度

  • 时间复杂度: O ( N ) O(N) O(N)
    • 预处理 suffixMin 数组需要 O ( N ) O(N) O(N)
    • 主循环遍历 nums[j] 需要 O ( N ) O(N) O(N)
    • 总体为 O ( N ) + O ( N ) = O ( N ) O(N) + O(N) = O(N) O(N)+O(N)=O(N)
  • 空间复杂度: O ( N ) O(N) O(N)
    • 需要 suffixMin 数组存储后缀最小值,空间为 O ( N ) O(N) O(N)

Code

class Solution {public int minimumSum(int[] nums) {int ret = Integer.MAX_VALUE;int n = nums.length;int[] suffixMin = new int[n];suffixMin[n - 1] = nums[n - 1];for (int i = n - 2; i >= 0; i--) {suffixMin[i] = Math.min(suffixMin[i + 1], nums[i]);}int prevMin = nums[0];for (int j = 1; j < n - 1; j++) {if (prevMin < nums[j] && nums[j] > suffixMin[j + 1]) {ret = Math.min(ret, (prevMin + nums[j] + suffixMin[j + 1]));}prevMin = Math.min(prevMin, nums[j]);}return ret == Integer.MAX_VALUE ? -1 : ret;}
}

1930. 长度为 3 的不同回文子序列

题目
题目截图2

思路

数组, 哈希表/计数

解题过程

一个长度为 3 的回文子序列形如 s[i]...s[j]...s[k] 变成 xyx,其中 s[i] == s[k]i < j < k
我们的目标是统计这种 xyx 形式的不同字符串的个数。

  1. 枚举外层字符:我们可以枚举构成回文的第一个字符 s[i] 和第三个字符 s[k]
  2. 字符频次预处理 (globalMap):首先,统计原字符串 s 中每个字符的出现次数,存储在 globalMap
  3. 外层循环 i:遍历 i0n-3 (作为第一个字符 s[i] 的索引)。
    • 剪枝:如果 globalMap[s[i] - 'a'] <= 1,说明字符 s[i] 在整个字符串中只出现了一次或零次,不可能作为回文的两端,直接跳过。
  4. 内层循环 k:对于每个 s[i],从字符串末尾 n-1 向前遍历 ki+1 (作为第三个字符 s[k] 的索引)。
    • 寻找匹配的 s[k]:当找到一个 s[k] == s[i] 时,我们就确定了回文的两个外层字符。
  5. 中层字符统计:在索引 ik 之间 (即 i+1k-1),我们需要找到所有不同的字符 s[j]
    • 使用一个局部的 map (大小为26的数组) 来记录在 (i, k) 区间内出现的不同字符。
    • 遍历 ji+1k-1。如果 s[j] 在这个 map 中是第一次出现 (即 map[s[j] - 'a'] < 1),则说明 s[i]s[j]s[k] 是一个新的回文子序列,ret 增加 1。
    • 同时,用 alike 记录在 (i, k) 区间内与 s[i] (或 s[k]) 相同的字符数量。
  6. 优化 (globalMap 更新与 break)
    • 一旦为当前的 s[i] 找到了最靠右的匹配 s[k] 并统计了中间的字符,我们就认为与这个 s[i](在当前索引 i 处的)相关的、以 s[k](在当前索引 k 处的)为结尾的回文都已经统计完毕。
    • 为了避免重复计算(特别是当 s[i] 字符在后续的 i 中再次出现时),我们从 globalMap 中减去已处理的字符:globalMap[s[i] - 'a'] -= (2 + alike)。这里的 2 代表 s[i]s[k]alike 代表中间与 s[i] 相同的字符。这是一种尝试性的优化,旨在减少后续迭代中对相同字符模式的重复探索。
    • 然后 break 内层 k 的循环,因为对于当前的 s[i],我们只关心它与最右边的 s[k] 形成的组合。
  7. 返回 ret

复杂度

  • 时间复杂度: O ( N 3 ) O(N^3) O(N3)
    • 外层 i 循环 N N N 次。
    • 内层 k 循环最坏情况下 N N N 次。
    • 中层 j 循环最坏情况下 N N N 次。
    • globalMap 的更新和 map 操作是 O ( 1 ) O(1) O(1) (因为字母表大小固定为26)。
    • 虽然 globalMap 的更新和 break 提供了一些剪枝,但最坏情况(例如字符串由许多不同字符组成,每个字符恰好出现两次)可能导致接近 O ( N 3 ) O(N^3) O(N3) 的行为。
  • 空间复杂度: O ( N ) O(N) O(N)
    • char[] s 占用 O ( N ) O(N) O(N)
    • globalMap 和局部的 map 占用 O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣),即 O ( 1 ) O(1) O(1)
    • 所以总体空间复杂度是 O ( N ) O(N) O(N)

Code

class Solution {public int countPalindromicSubsequence(String ss) {char[] s = ss.toCharArray();int ret = 0, n = s.length;int[] globalMap = new int[26];for (char c : s) {globalMap[c - 'a']++;}for (int i = 0; i < n - 2; i++) {if (globalMap[s[i] - 'a'] > 1) {for (int k = n - 1; k > i + 1; k--) {if (s[i] == s[k]) {int[] map = new int[26];int alike = 0;for (int j = i + 1; j < k; j++) {if (map[s[j] - 'a'] < 1) {ret++;}if (s[j] == s[i]) {alike++;}map[s[j] - 'a']++;}globalMap[s[i] - 'a'] -= 2 + alike;break;}}}}return ret;}
}

1433. 检查一个字符串是否可以打破另一个字符串(复习)

题目

题目截图3
这是第二次写这道题了,写的还不错,基本上算是掌握了,就不再多说了,详细题解见每日算法-250429

代码

class Solution {public boolean checkIfCanBreak(String ss1, String ss2) {char[] s1 = ss1.toCharArray();char[] s2 = ss2.toCharArray();Arrays.sort(s1);Arrays.sort(s2);boolean s1BreakS2 = true;for (int i = 0; i < s1.length; i++) {if (s2[i] > s1[i]) {s1BreakS2 = false;break;}}boolean s2BreakS1 = true;for (int i = 0; i < s1.length; i++) {if (s1[i] > s2[i]) {s2BreakS1 = false;break;}}return s1BreakS2 || s2BreakS1;}
}

682. 棒球比赛

题目

题目截图4
这是第二次写这道题了,写的还不错,基本上算是掌握了,就不再多说了,详细题解见每日算法-250330

代码

class Solution {public int calPoints(String[] operations) {Stack<String> stack = new Stack<>();for (String in : operations) {int a = 0, b = 0;switch (in) {case "+" -> {String num = stack.pop();a = Integer.parseInt(num);b = Integer.parseInt(stack.peek());stack.push(num);stack.push(String.valueOf(a + b));}case "D" -> {a = Integer.parseInt(stack.peek());stack.push(String.valueOf(2 * a));}case "C" -> stack.pop();default -> stack.push(in);}}int sum = 0;while (!stack.isEmpty()) {sum += Integer.parseInt(stack.pop());}return sum;}
}

相关文章:

  • 车载摄像头选型相关
  • 自定义异常小练习
  • QSS 使用方式介绍
  • cf2067A
  • 一个开源脚本,可自动安装在 AMD Radeon 7900XTX 上运行选定 AI 接口所需的所有内容
  • 大数据治理体系构建与实践路径
  • 文科小白学习Linux系统之安全管理
  • Debian:自由操作系统的精神图腾与技术基石
  • 12:遨博机器人开发
  • K6 是什么
  • 变量的计算
  • 25 字符数组与字符串及多维数组详解:定义与初始化、访问与遍历、%s 格式符、内存剖析、编程实战
  • 2025.5.26【ZR NOI模拟赛 T2】草莓函数 题解(性质,二分图最大权匹配)
  • (NAT64)IPv6网络用户访问IPv4网络服务器(动态映射方式)
  • 微内核与宏内核有什么区别(GAI)
  • Java -- 并发编程
  • 项目管理进阶:精读78页 IPD+CMMI+Scrum一体化研发管理解决方案【附全文阅读】
  • matlab雷达定位仿真
  • 约瑟夫问题
  • 企业级网络管理实战:Linux、云与容器的深度融合与优化
  • 济宁中小企业网站建设/廊坊关键词排名首页
  • 女性门户网站织梦模板/购物网站
  • 两学一做是什么网站/seo项目
  • 合肥有哪些公司是做网站的/长沙百度
  • 淘宝联盟网站怎么做/郑州疫情最新情况
  • 做网站流程 优帮云/南昌seo公司