算法入门-贪心1

第八部分:贪心

409.最长回文串(简单)

给定一个包含大写字母和小写字母的字符串 s ,返回通过这些字母构造成的最长的回文串 的长度。

在构造过程中,请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串。

示例 1:

输入:s = "abccccdd"
输出:7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。

示例 2:

输入:s = "a"
输出:1
解释:可以构造的最长回文串是"a",它的长度是 1。

第一种思路:

看到这种需要统计数量的,不自然的会想到使用字典的数据结构,按照这种思路,我考虑如下:

  1. 空字符串检查

  • 首先检查输入字符串 s 是否为空。如果为空,直接返回0,因为没有字符可以构成回文串。

  1. 字符计数

  • 使用一个 HashMap 来统计字符串中每个字符的出现次数。遍历字符串中的每个字符,更新其在 map 中的计数。

  1. 计算回文串长度

  • 初始化 sum 为0,用于存储可以构成的最长回文串的长度。

  • 遍历map中的每个字符及其出现次数:

    • 如果出现次数是偶数,则可以完全加入到回文串中,直接加到 sum

    • 如果出现次数是奇数,则将其减一后加入到 sum,并标记存在奇数值的键,以便后续可以在回文串的中心添加一个字符。

  1. 中心字符处理

  • 如果存在奇数值的键,说明可以在回文串的中心添加一个字符,因此在 sum 上加1。

  1. 返回结果

  • 最后返回 sum,即为通过给定字符串构造的最长回文串的长度。

import java.util.HashMap;  
import java.util.Map;  class Solution {  public int longestPalindrome(String s) {  // 检查字符串是否为空  if (s.length() == 0) {  return 0; // 如果字符串为空,直接返回0  }  int sum = 0; // 用于存储可以构成的最长回文串的长度  boolean hasOddValue = false; // 用于跟踪是否存在奇数值的键  Map<Character, Integer> map = new HashMap<>(); // 创建一个HashMap来存储字符及其出现次数  // 遍历字符串中的每个字符  for (char ch : s.toCharArray()) {  // 更新字符的出现次数  map.put(ch, map.getOrDefault(ch, 0) + 1);  }  // 遍历字符计数的映射  for (Map.Entry<Character, Integer> entry : map.entrySet()) {  int value = entry.getValue(); // 获取当前字符的出现次数  if (value % 2 == 0) { // 如果出现次数是偶数  sum += value; // 偶数部分可以完全加入到回文串中  } else { // 如果出现次数是奇数  sum += (value - 1); // 奇数部分减一后加入到回文串中  hasOddValue = true; // 标记存在奇数值的键  }  }  // 如果存在奇数值的键,则可以在回文串的中心添加一个字符  if (hasOddValue) {  sum += 1; // 增加1以考虑中心字符  }  return sum; // 返回计算得到的最长回文串长度  }  // 辅助方法:检查字符串中的所有字符是否相同  private boolean allCharactersSame(String s) {  char firstChar = s.charAt(0); // 获取第一个字符  for (int i = 1; i < s.length(); i++) {  if (s.charAt(i) != firstChar) {  return false; // 如果发现不同的字符,返回false  }  }  return true; // 所有字符相同,返回true  }  
}

第二种思路: 采用官方的贪心思路(不管我暂时还没有从官方的解释中体会到贪心体现在哪里)

解释:那么我们如何通过给定的字符构造一个回文串呢?我们可以将每个字符使用偶数次,使得它们根据回文中心对称。在这之后,如果有剩余的字符,我们可以再取出一个,作为回文中心。

于每个字符 ch,假设它出现了 v 次,我们可以使用该字符 v / 2 * 2 次,在回文串的左侧和右侧分别放置 v / 2 个字符 ch,其中 / 为整数除法。例如若 "a" 出现了 5 次,那么我们可以使用 "a" 的次数为 4,回文串的左右两侧分别放置 2 个 "a"。

如果有任何一个字符 ch 的出现次数 v 为奇数(即 v % 2 == 1),那么可以将这个字符作为回文中心,注意只能最多有一个字符作为回文中心。在代码中,我们用 ans 存储回文串的长度,由于在遍历字符时,ans 每次会增加 v / 2 * 2,因此 ans 一直为偶数。但在发现了第一个出现次数为奇数的字符后,我们将 ans 增加 1,这样 ans 变为奇数,在后面发现其它出现奇数次的字符时,我们就不改变 ans 的值了。

详情见:409. 最长回文串 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/longest-palindrome/

  1. 字符计数

    • 使用一个长度为128的数组 count 来统计字符串中每个字符的出现次数。数组的索引对应字符的ASCII值。

    • 遍历字符串 s,对于每个字符 c,增加 count[c] 的值。

  2. 计算回文串长度

    • 初始化 ans 为0,用于存储可以构成的最长回文串的长度。

    • 遍历count数组,对于每个字符的出现次数v:

      • 使用 v / 2 * 2 计算出可以组成的偶数部分,并加到 ans 中。这样可以确保回文串的左右两边是对称的。

      • 如果 v 是奇数,并且当前的 ans 是偶数,说明可以在回文串的中心添加一个字符(即使得回文串的长度增加1),因此将 ans 加1。

  3. 返回结果

    • 最后返回 ans,即为通过给定字符串构造的最长回文串的长度。

class Solution {  public int longestPalindrome(String s) {  // 创建一个长度为128的数组,用于统计字符的出现次数  int[] count = new int[128];  int length = s.length();  // 遍历字符串,统计每个字符的出现次数  for (int i = 0; i < length; ++i) {  char c = s.charAt(i);  count[c]++; // 增加字符c的计数  }  int ans = 0; // 用于存储最长回文串的长度  // 遍历计数数组,计算可以构成的回文串长度  for (int v : count) {  ans += v / 2 * 2; // 将偶数部分直接加到ans中  // 如果当前字符的出现次数为奇数,并且ans是偶数,则可以加1  if (v % 2 == 1 && ans % 2 == 0) {  ans++; // 可以在回文串的中心添加一个字符  }  }  return ans; // 返回计算得到的最长回文串长度  }  
}

455.分发饼干(简单)

题目:假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是满足尽可能多的孩子,并输出这个最大数值。

示例 1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释: 
你有三个孩子和两块小饼干,3 个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是 1,你只能让胃口值是 1 的孩子满足。
所以你应该输出 1。

示例 2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释: 
你有两个孩子和三块小饼干,2 个孩子的胃口值分别是 1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出 2。

第一种思路: 首先采用之前刷题的经验,先把这两个数组进行排序,减少工作量。

然后使用双指针的方法遍历两个数组,避免双重循环。

具体而言:

  1. 排序:首先对孩子的胃口数组 g 和饼干的大小数组 s 进行排序。这是为了能够从最小的胃口和最小的饼干开始进行比较,确保尽可能多的孩子能够得到满足。

  2. 双指针法:使用两个指针 childIndexcookieIndex 分别指向孩子和饼干的当前索引。childIndex 用于遍历孩子,cookieIndex 用于遍历饼干。

  3. 满足条件

    • 如果当前饼干可以满足当前孩子的胃口,即 g[childIndex] <= s[cookieIndex],则增加满足的孩子计数 contentChildrenCount,并同时移动到下一个孩子和下一个饼干。

    • 如果当前饼干不能满足当前孩子,则只移动到下一个饼干,继续尝试满足当前孩子。

  4. 结束条件:当遍历完所有孩子或所有饼干时,循环结束,返回满足的孩子数量。

class Solution {  public int findContentChildren(int[] g, int[] s) {  // 对孩子的胃口和饼干的大小进行排序  Arrays.sort(g);  Arrays.sort(s);  int contentChildrenCount = 0; // 记录满足的孩子数量  int childIndex = 0; // 当前孩子的索引  int cookieIndex = 0; // 当前饼干的索引  // 遍历孩子和饼干,直到其中一个数组遍历完  while (childIndex < g.length && cookieIndex < s.length) {  // 如果当前饼干可以满足当前孩子的胃口  if (g[childIndex] <= s[cookieIndex]) {  contentChildrenCount++; // 满足一个孩子  childIndex++; // 移动到下一个孩子  }  // 无论饼干是否满足孩子,都要移动到下一个饼干  cookieIndex++;  }  return contentChildrenCount; // 返回满足的孩子数量  }  
}

后面发现官方的解答和我类似,但是他就是使用双重循环的,想了一下。

贪心算法的贪心在这里面体现在哪?

在这个问题中,贪心算法的贪心策略体现在以下几个方面:

  1. 局部最优选择

    • 每次选择当前最小的饼干来满足当前最小的孩子的胃口。这种选择是基于局部最优的原则,即在每一步中都选择能够立即满足当前孩子的最小饼干。通过这种方式,尽可能多的孩子能够得到满足。

  2. 排序

    • 通过对孩子的胃口和饼干的大小进行排序,确保我们可以从最小的开始进行比较。这种排序使得我们能够有效地找到最小的满足条件的饼干,从而实现贪心选择。

  3. 不回溯

    • 一旦选择了一个饼干来满足一个孩子,就不会回头去尝试用这个饼干去满足其他孩子。每次都向前推进,确保每个孩子都尽可能得到满足,而不去重新考虑之前的选择。

  4. 全局最优解的构建

    • 通过不断地做出局部最优选择(即用当前最小的饼干满足当前最小的孩子),最终构建出全局最优解(即最大数量的孩子得到满足)。这种策略在许多贪心算法中都是常见的。

总结

贪心算法在这个问题中的核心思想是通过局部最优选择(最小饼干满足最小胃口)来达到全局最优解(最大数量的孩子得到满足)。这种方法简单高效,适合解决此类问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/141951.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++ ——string的模拟实现

目录 前言 浅记 1. reserve&#xff08;扩容&#xff09; 2. push_back&#xff08;尾插&#xff09; 3. iterator&#xff08;迭代器&#xff09; 4. append&#xff08;尾插一个字符串&#xff09; 5. insert 5.1 按pos位插入一个字符 5.2 按pos位插入一个字符串 …

2024年全国大学生数学建模竞赛B题生产过程中的决策问题分析

目录 引言 问题 1&#xff1a;抽样检测方案设计 问题 2&#xff1a;生产过程中的决策 决策阶段划分 决策方案 结果 问题 3&#xff1a;多道工序和零配件的决策 生产流程 决策过程 问题 4&#xff1a;基于抽样检测的重新决策 动态调整次品率 结论 引言 在2024年全国…

Python 的 WSGI 简单了解

从 flask 的 hello world 说起 直接讨论 WSGI&#xff0c;很多人可能没有概念&#xff0c;我们还是先从一个简单的 hello world 程序开始吧。 from flask import Flaskapp Flask(__name__)app.route("/", methods[GET]) def index():return "Hello world!&q…

【Axure原型】B端系统登录注册页设计成这样,就不用跟小孩一桌了

前言 在B端后台中&#xff0c;登录注册页这个东西&#xff0c;因为感觉很简单&#xff0c;所以经常不被产品经理们重视。但是登录注册页作为一个后台系统的门面&#xff0c;直接影响用户第一印象&#xff0c;又是非常重要的存在。 登录注册页的价值 B端系统登录注册页是用户…

极验3代前两个参数w逆向分析

声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关。 本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除! 前言 这次会简单的讲解…

Java封装(面向对象)

这个是大三来的时候刚开始又捡起来了java这个语言&#xff0c;以前是开设过这个课程的&#xff0c;但是上课都没哈好听讲&#xff0c;现在又开始学习java类的。 首先附上黑马程序员的一张听课截图吧 我就拿我在黑马程序员的例子上给自己的一个讲解吧 首先是javabean类 pack…

我在高职教STM32——准备HAL库工程模板(2)

新学期已开始,又要给学生上 STM32 嵌入式课程了。这课上了多年了,一直用的都是标准库来开发,已经驾轻就熟了。人就是这样,有了自己熟悉的舒适圈,就很难做出改变,老师上课也是如此,排斥新课和不熟悉的内容。显然,STM32 的开发,HAL 库已是主流,自己其实也在使用,只不过…

【IEEE出版,高录用 | EI快检索】第二届人工智能与自动化控制国际学术会议(AIAC 2024,10月25-27)

第二届人工智能与自动化控制国际学术会议&#xff08;AIAC 2024&#xff09; The 2nd International Conference on Artificial Intelligence and Automation Control 2024年10月25-27日 中国-广州 重要信息 会议官网&#xff1a;www.icaiac.org The 2nd International Conf…

网络安全学习(三)Hydra破解密码

接下来看一下Hydra工具&#xff0c;这是一个暴力破解密码的工具。 使用命令&#xff08;注意区分大小写&#xff09;。 hydra -L user.txt账号字典 -P pass.txt密码字典 IP地址 smb协议名称 hydra -l administrator指定账号 -P pass.txt密码字典 IP地址 smb协议名称 hydra -…

进程状态、进程创建和进程分类

文章目录 进程进程常见的状态进程调度进程状态变化关系 进程标识示例--进程标识的使用以及简介 进程创建fork函数vfork函数示例--使用fork函数创建子进程&#xff0c;并了解进程之间的关系 创建进程时发生的变化虚拟内存空间的变化示例--验证fork函数创建进程时的操作 对文件IO…

改进RRT*的路径规划算法

一、RRT算法 RRT 算法是一种基于随机采样的快速搜索算法。该算法的主要思想是通过随机采样来创建一个快速探索的树&#xff0c;从而生长出一条从起点到终点的路径。如图为随机树的生长过程。 初始化。首先&#xff0c;初始化起始点和目标点位置&#xff0c;并将起点作为根节点…

5-----RYZ维修工具 操作界面预览与功能操作解析 刷机 解锁 修复参数等等

以上是工具选项功能的界面预览 。通过预览可以看到很多功能选项。此类工具涵盖了很多操作区域。需要根据自己机型的实际需求来操作。根据开发者的描述。此工具有一下功能。包含mtk刷机 分区修复。9008刷机 备份基带efs等等。 高通操作区域 高通修复串码 高通修改写入基带qc…

python中的各类比较与计算

运算符 1.算数运算符2.关系运算符3.逻辑运算符4.关于短路求值5.赋值运算符1&#xff09;的使用链式赋值多元赋值 2)复合赋值运算符 6.位运算符7.成员运算符8.身份运算符 1.算数运算符 # 加 print(1 2) # 减 print(2 - 1) # 乘 print(1 * 2) # 余数 4%31余数为1 print(4 % 3…

只要不逾期就行了吗?如何守护好你的“第二张身份证“!

在这个时代&#xff0c;信用记录已远远超越了金融交易的范畴&#xff0c;它如同一根无形的纽带&#xff0c;将我们生活的各个领域紧密相连。近闻有人甚至在步入婚姻殿堂前&#xff0c;也要细致核查对方的信用状况&#xff0c;毕竟&#xff0c;这关乎到共同生活的基石与未来幸福…

通过hosts.allow和hosts.deny限制用户登录

1、Hosts.allow和host.deny说明 两个文件是控制远程访问设置的&#xff0c;通过设置这个文件可以允许或者拒绝某个ip或者ip段的客户访问linux的某项服务。如果请求访问的主机名或IP不包含在/etc/hosts.allow中&#xff0c;那么tcpd进程就检查/etc/hosts.deny。看请求访问的主机…

volatile 实现原理了解吗?

volatile 实现原理了解吗&#xff1f; volatile 有两个作用&#xff0c;保证可见性和有序性。 volatile 怎么保证可见性的呢&#xff1f; 简单来说&#xff1a;读取和写入变量从原本的本地内存变成主内存中 相比 synchronized 的加锁方式来解决共享变量的内存可见性问题&#…

网络原理 IP协议与以太网协议

博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:MySQL数据库 JavaEE专栏:JavaEE 关注博主带你了解更多数据结构知识 目录 1.网络层 IP协议 1.IP协议格式 2.地址管理 2.1 IP地址 2.2 解决IP地址不够用的问题 2.3NAT网络地址转换 2.4网段划分 3.路由选择…

Unity多国语言支持

Unity多国语言支持 项目在我的课程 ”淘金城堡“ 中应用 项目的地址&#xff1a;http://t.csdnimg.cn/m0hFd 一、基本概念 在Unity中加入多国语言的支持可以让我们发布的游戏或应用上线在拥有不同语言的国家或地区。 下面介绍一款Unity官方提供的插件“Localization package…

FinLex An effective use of word embeddings for financial lexicon generation

FinLex: An effective use of word embeddings for financial lexicon generation 论文阅读 文章目录 FinLex: An effective use of word embeddings for financial lexicon generation 论文阅读 AbstractMethodology具体词表例子LM 词列表与 FinLex 词列表 词列表在分类任务中…

EMT-DAVT--基于子空间分布对齐和决策变量转移的多目标多任务优化

EMT-DAVT–基于子空间分布对齐和决策变量转移的多目标多任务优化 title&#xff1a; Multiobjective Multitasking Optimization With Subspace Distribution Alignment and Decision Variable Transfer author&#xff1a; Weifeng Gao, Jiangli Cheng, Maoguo Gong, Hong L…