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

安吉网站建设公司网站后台管理系统

安吉网站建设公司,网站后台管理系统,咸阳网站建设推广,网络规划设计师教程下载记录今天的算法学习与复习。 1234. 替换子串得到平衡字符串 题目 思路 核心思路: 滑动窗口 解题过程 根据题意,一个字符串 s 被认为是平衡的,当且仅当 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符在 s 中出现的次数都相等(即 n/4)。…

记录今天的算法学习与复习。


1234. 替换子串得到平衡字符串

题目

题目截图

思路

核心思路: 滑动窗口

解题过程

根据题意,一个字符串 s 被认为是平衡的,当且仅当 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符在 s 中出现的次数都相等(即 n/4)。我们需要找到一个最短的子串,替换它之后能让整个字符串变平衡。

反向思考:如果我们要替换子串 s[l..r],那么这个子串 之外 的字符 s[0...l-1]s[r+1...n-1] 是保持不变的。为了使 整个 字符串最终平衡,这些 不变 的字符中,每种字符 (‘Q’, ‘W’, ‘E’, ‘R’) 的数量都不能超过 n/4。如果某个字符在子串外部的数量已经超过 n/4,那么无论我们如何修改子串 s[l..r],都无法使该字符的总数降到 n/4,也就无法使整个字符串平衡。

因此,我们的目标是找到一个最短的窗口 [left, right](代表要替换的子串),使得窗口 之外 的所有字符计数 (total_count[char] - window_count[char]) 均 <= n/4

我们可以使用滑动窗口来解决这个问题:

  1. 先统计整个字符串 s 中各字符的出现次数 hash[c]
  2. 如果初始状态已经是平衡的,直接返回 0。
  3. 初始化 left = 0, right = 0。扩展右边界 right,并将 s[right] 的计数从 hash 中减去(表示这个字符现在在窗口内,不属于 “窗口外” 的部分了)。
  4. 检查当前窗口是否满足条件:即窗口外的所有字符计数 (hash['Q'], hash['W'], hash['E'], hash['R']) 是否都 <= n/4
  5. 如果满足条件,说明当前窗口 [left, right] 是一个潜在的可替换子串。我们尝试缩小窗口以找到最短长度:记录当前窗口长度 right - left + 1 并更新最小值 ret;然后将 s[left] 的计数加回 hash(表示 s[left] 移出窗口,回到 “窗口外” 的部分),并将 left 右移。重复此步骤直到条件不再满足。
  6. 继续扩展右边界 right,重复步骤 3-5,直到 right 到达字符串末尾。
  7. 最终 ret 中存储的就是最短的可替换子串长度。

复杂度

  • 时间复杂度: O ( n ) O(n) O(n),其中 n 是字符串的长度。每个字符最多被 leftright 指针访问两次。
  • 空间复杂度: O ( C ) O(C) O(C) O ( 1 ) O(1) O(1),其中 C 是字符集的大小 (QWER,为常数)。我们使用了一个固定大小的哈希数组来存储字符计数。

Code

import java.util.Arrays;class Solution {public int balancedString(String ss) {char[] s = ss.toCharArray();int n = s.length;// m 是每个字符的目标数量int m = n / 4;// ret 初始化为一个不可能的最大值int ret = n;// 统计整个字符串的字符计数int[] hash = new int[128];for (char c : s) {hash[c]++;}// 检查初始字符串是否已经平衡if (hash['Q'] == m && hash['W'] == m && hash['E'] == m && hash['R'] == m) {return 0;}// 滑动窗口for (int left = 0, right = 0; right < n; right++) {// 字符 s[right] 进入窗口,将其从 "窗口外计数" 中移除hash[s[right]]--;// 检查窗口外的字符计数是否都满足 <= m// 如果满足,尝试缩小窗口while (left <= right && hash['Q'] <= m && hash['W'] <= m && hash['E'] <= m && hash['R'] <= m) {// 更新最短窗口长度ret = Math.min(ret, right - left + 1);// 字符 s[left] 离开窗口,将其加回 "窗口外计数"hash[s[left]]++;// 缩小窗口左边界left++;}}// 最终结果return ret;}
}

682. 棒球比赛

题目

题目截图

思路

核心思路: 模拟 / 栈 (可以用数组模拟)

解题过程

题目描述了一个计分系统,包含四种操作。我们可以使用一个列表或数组来模拟记录有效得分的过程,其行为类似于栈:

  1. 遍历操作数组 ops
  2. 维护一个记录有效分数的列表(或数组和指针)。
  3. 根据当前操作 ops[i] 执行相应逻辑:
    • 如果是整数 x:将其转换为数字,添加到分数列表末尾。
    • 如果是 "+":取列表末尾的两个分数,求和,并将和添加到列表末尾。
    • 如果是 "D":取列表末尾的分数,乘以 2,并将结果添加到列表末尾。
    • 如果是 "C":移除列表末尾的分数(使其无效)。
  4. 遍历结束后,计算分数列表中所有分数的总和。

在提供的代码中,使用了数组 nums 和指针 index 来模拟这个过程,index 指向下一个可以插入元素的位置,同时也表示当前有效分数的数量。

复杂度

  • 时间复杂度: O ( n ) O(n) O(n),其中 n 是操作数组 ops 的长度。需要遍历一次数组。
  • 空间复杂度: O ( n ) O(n) O(n),在最坏的情况下(例如所有操作都是数字),需要存储 n 个分数。

Code

class Solution {public int calPoints(String[] ops) {int n = ops.length;// 使用数组模拟栈来存储有效分数int[] nums = new int[n];// index 指向下一个可插入位置,也代表当前有效分数数量int index = 0;for (int i = 0; i < n; i++) {switch (ops[i]) {case "C":// 使上一个分数无效,index 后退// 注意:这里直接将值设为 0 也可以,但 index-- 更符合栈的移除操作// 后面求和时,无效位置的值不应被计算,但因为 index 减少了,循环求和时自然会跳过if (index > 0) { // 防止对空栈执行 C 操作index--;// 可选:nums[index] = 0; 清零非必需,因为求和只到 index}break;case "D":// 将上一个分数翻倍并压入if (index > 0) { // 确保有上一个分数nums[index] = nums[index - 1] * 2;index++;}break;case "+":// 将最后两个分数的和压入if (index >= 2) { // 确保有最后两个分数nums[index] = nums[index - 1] + nums[index - 2];index++;}break;default:// 是数字,直接解析并压入int val = Integer.parseInt(ops[i]);nums[index++] = val;break;}}// 计算所有有效分数的总和int sum = 0;for (int i = 0; i < index; i++) { // 只对 index 之前的有效分数求和sum += nums[i];}return sum;}
}

53. 最大子数组和(复习)

题目

题目截图

复习笔记

这次重写依然采用动态规划(或者说是在线处理)的思路。核心思想是:对于数组中的每一个元素 nums[i],以它结尾的最大子数组和要么是 nums[i] 本身(即从 nums[i] 开始一个新的子数组),要么是 nums[i] 加上以 nums[i-1] 结尾的最大子数组和(即将 nums[i] 加入到前面的子数组中)。

我们用 curSum 记录以当前元素结尾的最大子数组和。状态转移方程为:
curSum = Math.max(nums[i], curSum + nums[i])

同时,我们需要一个变量 maxSum 来记录遍历过程中遇到的所有 curSum 的最大值,这就是全局的最大子数组和。

这次复习过程中,对这个状态转移的理解和应用还算顺利,但需要确保完全内化,做到不假思索地写出。

具体解题思路回顾请看 3月23号的算法题。

复杂度

  • 时间复杂度: O ( n ) O(n) O(n),只需要一次遍历。
  • 空间复杂度: O ( 1 ) O(1) O(1),只使用了常数个额外变量。

Code

class Solution {public int maxSubArray(int[] nums) {// 初始化 curSum 和 maxSum 为第一个元素的值int curSum = nums[0];int maxSum = nums[0];// 从第二个元素开始遍历for (int i = 1; i < nums.length; i++) {// 决定是开启新子数组还是加入原子数组curSum = Math.max(nums[i], curSum + nums[i]);// 更新全局最大和maxSum = Math.max(curSum, maxSum);}return maxSum;}
}

1652. 拆炸弹(复习)

题目

题目截图

复习笔记

解题方法依然是滑动窗口。关键在于处理 k 的三种情况:

  1. k == 0: 结果数组所有元素都是 0。
  2. k > 0: 对于 code[i],需要求和 code[i+1]code[i+k](注意数组循环)。
  3. k < 0: 对于 code[i],需要求和 code[i-1]code[i+k](即 code[i-|k|]code[i-1], 注意数组循环)。

为了统一处理 k > 0k < 0 的情况,代码中使用了一个技巧:

  • k < 0 时,先将原数组 code 反转,然后令 a = |k|。这样问题就转换成了求解反转后数组的 “向后” a 个元素的和。
  • 计算完结果数组 ret 后,如果 k < 0,再将 ret 反转回来,得到最终答案。

这样,主要的计算逻辑就只需要处理 k > 0(或等价的 a > 0)的情况,即计算每个位置之后 a 个元素的和。这可以通过滑动窗口实现:

  • 先计算出 ret[0] 的值,即 code[1]code[a] 的和 (注意处理循环下标)。
  • 然后窗口向右滑动,对于 ret[i] (当 i > 0),其和可以通过 ret[i-1] 的和减去 code[i](移出窗口的元素)并加上 code[i+a](移入窗口的元素)得到 (注意处理循环下标)。

本次复习遇到的问题:

  • 忘记在处理 k < 0 之前,无论 k 正负,都应该先用 a 保存 k 的绝对值或原始值(根据后续逻辑决定)。代码中是在 a < 0 时才赋值,应在开始就处理 a = Math.abs(k) 或类似逻辑。(修正后的代码在判断 k<0 后设置 a = -k,并在反转数组后使用 a 作为正数窗口大小,是正确的处理方式)
  • 滑动窗口更新 sumret[left] 的逻辑细节:sum 的更新应该是 sum = sum - code[left] + code[right],并且 ret[left] 应该在 sum 更新 之后 赋值(或者在更新前赋值 ret[left-1] 的结果)。代码中的 sum += (code[right] - code[left])ret[left] = sum; left++; 逻辑是正确的,它计算的是下一个位置 left 的结果。

具体解法回顾请看 3月23号的算法题。

复杂度

  • 时间复杂度: O ( n ) O(n) O(n),滑动窗口遍历一次。如果 k < 0,两次数组反转也是 O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n),需要一个额外的数组 ret 来存储结果。如果允许原地修改(题目通常不允许),可以做到 O ( 1 ) O(1) O(1)

Code

class Solution {public int[] decrypt(int[] code, int k) {int n = code.length;int[] ret = new int[n];// k == 0 的情况if (k == 0) {// 结果数组默认为 0,无需操作return ret;}// 为了统一处理 k > 0 和 k < 0,我们用 a 代表窗口大小 (绝对值)int a = Math.abs(k);// 如果 k < 0,先反转数组,问题变为向后求和if (k < 0) {reverse(code);}// 计算初始窗口 [1, a] 的和 (注意下标循环)int sum = 0;for (int i = 1; i <= a; i++) {// 使用模运算处理循环下标sum += code[i % n];}// 计算 ret[0]ret[0] = sum;for (int i = 1; i < n; i++) {sum = sum - code[i % n] + code[(i + a) % n];ret[i] = sum;}if (k < 0) {reverse(ret);}return ret;}// 辅助函数:反转数组private void reverse(int[] arr) {int left = 0, right = arr.length - 1;while (left < right) {int tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}}
}
http://www.dtcms.com/wzjs/448997.html

相关文章:

  • 江夏网站建设网络媒体发稿平台
  • 怎么开网店新手入门拼多多店铺北京网站建设优化
  • 学动漫设计我后悔了windows11优化大师
  • 湖南省郴州市嘉禾县网站优化服务
  • 北京网站开发公司有哪些谷歌广告推广
  • 手机做wifi中继上外国网站百度一下百度主页度
  • 青岛即墨网站建设设计快速整站排名seo教程
  • 胶州为企业做网站的公司酒吧营销用什么软件找客源
  • 微网站开发平台 知乎百度网站登录入口
  • 做dw网站图片怎么下载北京百度快速排名
  • wordpress怎么做淘客网站百度浏览器网址链接
  • 网络文化经营许可证要多少钱seo每日
  • 校园网站建设招标公告搜索引擎平台
  • 重庆网站建设公司seo计费系统登录
  • 做女装批发哪个网站好推广平台免费b2b网站大全
  • wordpress 模板修改北京优化网站公司
  • 有哪些做兼职的设计网站有哪些工作内容百度网络优化推广公司
  • 建设网站用什么服务器重庆seo教程搜索引擎优化
  • 高邮城乡建设局 网站搜索引擎营销的四种方式
  • 最强的网站建设电话seo实战优化
  • php和ASP网站那个好seo零基础入门到精通200讲
  • 用HBuilder做网站的模板电子商务是干什么的
  • 内蒙古企业网站制作鸿星尔克网络营销
  • 什么是网站集约化建设长沙seo平台
  • python wordpress 外链谷歌seo运营
  • 为什么做视频网站违法推广网站排名优化seo教程
  • 贵阳市做网站的公司有哪些公司官网制作开发
  • 泉州制作网站软件seo咨询推广找推推蛙
  • 做网站的公司介绍网站收录免费咨询
  • 专业做网站建设的公司西安seo优化