Hang's Blog
type: channel
link: https://hang.domcer.com/
description: Hang's Blog
items: UltraFunction | 我的世界插件低代码平台
items: Java Design | AbstractFactory
items: Java Design | AbstractDocument
items: Java | 代码简洁规范
items: 预警方案设计与实现
items: LeetCode | [1276]-Medium 不浪费原料的汉堡制作方案
items: Elasticsearch | 聚合 (按日统计、根据字段去重等)
items: LeetCode | [20]-Easy 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效
items: LeetCode | [14]-Easy 编写一个函数来查找字符串数组中的最长公共前缀
items: LeetCode | [13]-Easy 罗马数字转换成阿拉伯数字

UltraFunction | 我的世界插件低代码平台
type: item
date: 2024-10-25T14:45:00+08:00
description: 前言的前言最近私下做了好一阵子的项目 UltraFunction，以至于好久没更新过博客了（白天工作太忙了），特此过来总结一番。估摸着算了一下，这个项目从 2024/8/29 开始构建项目，到如今也快 2 个月了，中间发生了不少难题，都一一解决，为了自己的热爱（屹立不倒）在此感谢参与项目的所有人🙏🏻网址: https://ultrafunction.domcer.com/Wiki: https://ultrafunction.domcer.com/wikiDiscord: https://discord.gg/2YZuDJwpMXEmail: admin@domcer.comQQ: 217934413SpigotMC: https://www.spigotmc.org/resources/%E2%AD%90ultrafunction-low-code-development-platforms-1-8-1-21.120276/前言感觉国内貌似对低代码平台不感兴趣，在当前公司，也曾有过低代码平台（对于前端），当时闹得风风火火，领导都很看中。节后开发组的人就不见了踪影。（3名前端主力，听说都被优化了，我是当时支持的后端，瑟瑟发抖）在国外经常刷到一些视频，说一群孩子使用 iPad 做一些好玩的东西（应该很多年了），感觉国外一直都很力推技术教育，常常在孩子最具有创造力和想象力的年龄，去深挖他们的潜力。而国内貌似就没有这些内容了，甚至很难推进。24年有幸被网易邀请到上海喝茶聊天，我就提过少儿教育这类的事情，他们的回复的大概意思是，国内政府很难说通，很难办，如果在大学应该还好。可是我想说，如果到大学，他还真的会对这些感兴趣吗？这是一个什么项目Wiki: [这个项目致力于简化 Minecraft 插件的创建过程，通过低代码平台，让用户轻松生成自定义逻辑数据包插件。无需任何编程基础，只需选择预定好的模块，并配置相关行为，即可实现独特的游戏功能。无论是添加新的游戏机制，还是打造个性化的互动体验，我们的工具都能满足大部分的需求。直观的界面和强大的自定义选项，让每一位 Minecraft 爱好者都能轻松实现创意，提升游戏乐趣，快来打造属于您的专属世界吧！]说白了就是一个可以通过拖拽积木块的方式，来达到自己的逻辑行为。帮助那些不会写代码，但是想要做游戏内逻辑行为的玩家，提供一个社区、平台，帮助他们完成想要做的事情。名字和LOGO的来源对于定义这个项目，其实也想了一些时间，最终定下了 UltraFunction，这个不仅仅只有直译的 极致功能/极致方法，也有英文可拆解的 Ultra Fun（有趣）ction，让大家在使用这个平台的时候，更多感受到的是一种乐趣。关于技术选型和架构关于前端前端共有 2 个部分：官网页面、画布编辑官网页面采用的是 Vue画布编辑使用的是 Google Blockly：[Blockly 是 Google 推出的一款可视化编程编辑器，使用拖放块。它是为全球最受欢迎的编程教育计划提供支持的引擎。]因为我并不是做前端的，所以一些细节我也没办法讲述太多。。。关于后端后端共有 3 个部分：平台业务层、核心执行层、插件处理层平台业务层：采用 SpringBoot + MySQL 的设计，对平台的业务进行处理，包括但不限于（用户账号功能、工作台管理功能、文件触发器编辑功能等）核心执行层：采用 antlr 定义自己的领域语言（DSL），对接前端的 Blockly，通过积木块生成 DSL 内容插件处理层：使用 Bukkit 包进行开发，对文件、事件等进行统一分发和处理为什么选择 antlr性能实测每次都经过compile: 一组包含20条语句的代码块，有20个入参数与2个出参。每次执行都经过输入、编译、执行、输出四个步骤。循环执行1000000万次，时间是122212ms，平均每条执行时间0.12ms每次不经过compile: 一组包含20条语句的代码块，有20个入参数与2个出参。每次执行都经过输入、执行、输出三个步骤。循环执行1000000万次，时间是6151ms，平均每条执行时间0.006ms所以，可以将即将执行的代码块进行预编译，存储到缓存内，每次取其中的 ParserTree 直接运行高性能原理分析模块原理antlr4词法匹配DFAantlr4语法匹配Adative LL()词法分析（Lexical Analysis）词法分析器（Lexer）将输入的字符流分割成令牌（Tokens）。这一步类似于正则表达式的匹配，但ANTLR4使用的是其自己的模式匹配算法。词法分析器规则（Lexer rules）可以用类似正则表达式的语法来定义，但它们不是直接使用正则表达式库来实现的，而是ANTLR4自身的匹配机制。语法分析（Parsing）语法分析器（…

Java Design | AbstractFactory
type: item
date: 2024-05-29T16:51:00+08:00
description: IntentProvide an interface for creating families of related or dependent objects without specifying[指定] their concrete[具体] classes.ExplanationReal-world exampleImagine a furniture[家具] company that produces various styles of furniture: modern, Victorian[维多利亚时代], and rustic[乡村的]. Each style includes products like chairs, tables, and sofas. To ensure[确保] consistency[一致性] within each style, the company uses an Abstract Factory pattern.In this scenario[脚本], the Abstract Factory is an interface for creating families of related furniture objects (chairs, tables, sofas). Each concrete factory (ModernFurnitureFactory, VictorianFurnitureFactory, RusticFurnitureFactory) implements the Abstract Factory interface and creates a set of products that match the specific style. This way, clients can create a whole set of modern or Victorian furniture without worrying about the details of their instantiation[实例化]. This maintains[保持] a consistent style and allows easy swapping[交换] of one style of furniture for another.In plain wordsA factory of factories; a factory that groups the individual[单独的] but related/dependent factories together without specifying their concrete classes.Wikipedia saysThe abstract factory pattern provides a way to encapsulate[概括、封装] a group of individual factories that have a common theme without specifying their concrete classesProgrammatic ExampleTo create a kingdom we need objects with a common theme. The elven[精灵] kingdom needs an elven king, elven castle, and elven army whereas[而、反之] the orcish[兽人] kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom.public interface Castle { String getDescription(); } public interface King { String getDescription(); } public interface Army { String getDescription(); } // Elven implementations -> public class ElfCastle implements Cast…

Java Design | AbstractDocument
type: item
date: 2024-05-28T18:47:00+08:00
description: 意图 Intent抽象文档设计模式是一种结构设计模式，旨在通过为各种文档类型定义公共接口，提供一种一致的方式来处理分层和树状数据结构。它将核心文档结构与特定的数据格式分离，实现动态更新和简化维护。The Abstract Document design pattern is a structural design pattern that aims to provide a consistent way to handle hierarchical and tree-like data structures by defining a common interface for various document types. It separates the core document structure from specific data formats, enabling dynamic updates and simplified maintenance.解释 Explanation抽象文档模式允许处理附加的非静态属性。此模式使用特性的概念来实现类型安全，并将不同类的属性分离到一组接口中。The Abstract Document pattern enables handling additional, non-static properties. This pattern uses concept of traits to enable type safety and separate properties of different classes into set of interfaces.真实世界的例子 Real world example想象一下，在一个图书馆系统中，书籍可以有不同的格式和属性：实体书、电子书和有声读物。每种格式都有独特的属性，如实体书的页数、电子书的文件大小和有声读物的持续时间。摘要文档设计模式允许图书馆系统灵活地管理这些不同的格式。通过使用这种模式，系统可以动态存储和检索属性，而不需要为每种书籍类型提供刚性结构，从而在未来更容易添加新的格式或属性，而不会对代码库进行重大更改。Imagine a library system where books can have different formats and attributes: physical books, eBooks, and audiobooks. Each format has unique properties, such as page count for physical books, file size for eBooks, and duration for audiobooks. The Abstract Document design pattern allows the library system to manage these diverse formats flexibly. By using this pattern, the system can store and retrieve properties dynamically, without needing a rigid structure for each book type, making it easier to add new formats or attributes in the future without significant changes to the codebase.用简单的话说 In plain words抽象文档模式允许在对象不知情的情况下将属性附加到对象。Abstract Document pattern allows attaching properties to objects without them knowing about it.维基百科称 Wikipedia says一种面向对象的结构设计模式，用于在松散类型的键值存储中组织对象，并使用类型化视图公开数据。该模式的目的是在强类型语言中的组件之间实现高度的灵活性，在这种语言中，可以动态地将新属性添加到对象树中，而不会失去类型安全的支持。该模式利用特征将类的不同属性分离到不同的接口中。An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing the data using typed views. The purpose of the pattern is to achieve a hig…

Java | 代码简洁规范
type: item
date: 2024-03-06T11:13:00+08:00
description: 前言做开发也有了一段时间，好的代码和烂的代码也看了很多，所以想记录下来平日里面自己开发的一些规范，方便后续其他人阅读学习或借鉴。之前有读过《代码整洁之道》，感觉受益匪浅，里面讲的很多内容都是日常会犯的一些小毛病，而且书中教了大家该如何让代码看起来更舒服，细节决定了更多事情，如果一个人写代码的风格换来换去，只会让这一片代码受到污染，即使是小的空格和空行，也需要多加注意。先记录下来，慢慢更新吧！或许是强迫症还是洁癖使然，让我对代码规范比较敏感例如这里是否多个空行，那里是否少个空格有问过其他同事，他们貌似对这些并无所谓，觉得能开发出来就好开门见山1. 空格所有特殊符号前后都应有空格，最常见的可以直接使用 idea 自带的代码整理，自动帮忙规范。规定：定义变量的等于号前后都需要有空格循环或者IF这种特殊词与括号都需要有空格来隔离。[scode type="red"]坏示例[/scode]String name="example"; int age= 1; int age =1; if(A ==B) if(A<B)[scode type="green"]好示例[/scode]String name = "example"; int age = 1; if (A == B)2. 类开始和结束的空行有很多人喜欢类开始的时候，直接开始写代码，在结束的时候又莫名奇妙多出来很多空行，亦或是在分割方法的时候没有空行，或者多个空行，这样都会让代码显得很不规范。规定：在新的类写代码时，应先空一行，再开始编写代码在类结束时，应该与上一个最近的方法紧贴，没有空行每个方法之间只且需要一个空行来隔离不同类型的变量应该用空行来隔离变量与方法之间应该用空行来隔离如果出现 List 类型的变量，应该以 xxxList 为命名，取消类似英文复数的定义，比如pages等，这种很容易让人引起歧义如果出现 Map 类型的变量，应该以 xxxMap 为命名[scode type="red"]坏示例[/scode]public class ApiRequestTotal extends BaseBasicDashboardCalculate { private static final String NAME = "example"; private String name; private String type; @Override public Map<String, Object> getConditions() { Map<String, Object> map = new HashMap<>(); map.put("finalDecisionCode", "Accept"); return map; } @Override public Map<String, Object> getNestedConditions() { return new HashMap<>(); } @Override public BasicDashboardMetricTypeEnum getMetricType() { return BasicDashboardMetricTypeEnum.API_REQUEST_TOTAL; } }[scode type="green"]好示例[/scode]public class ApiRequestTotal extends BaseBasicDashboardCalculate { private static final String NAME = "example"; private String name; private String displayName; @Override public Map<String, Object> getConditions() { Map<String, Object> map = new HashMap<>(); map.put("finalDecisionCode", "Accept"); return map; } @Override public Map<String, Object> getNestedConditions() { return new HashMap<>(); } @Override public BasicDashboardMetricTypeEnum getMetricType() { return BasicDashboardMetricTypeEnum.API_REQUEST_TOTAL; } }3. 属性和变量的定义这块的定义其实是很糟糕的，经常看到很多代码，打开一个类的时候，迎头一棒全是定义，导致要一直往下翻找代码，且定义的很不规范，一样类型的内容应该遵守从…

预警方案设计与实现
type: item
date: 2024-03-04T17:21:00+08:00
description: 前言最近成为了 Warning Master (调侃）因为最近接手了3个预警项目，都是要做各个类别数值统计计算指标的预警感觉挺有趣的，就想记录下来这三个项目分别为：某A外企啤酒公司某B国际航空公司某C会员超市公司[scode type="blue"]小小的项目背景[/scode]某A外企啤酒公司项目要做预警是由于在2023年底的时候爆出来了一个生产问题，是由于当时代码分支记录混乱，最后上线的包版本缺少了部分 commit 提交，最后引发了比较惨烈的生产问题，结果是领导也没有深究（毕竟是领导在分支上管理不太好，也没有同步消息，修复的BUG在一个bugfix分支，没有在项目分支上）。紧接着引发了客户投诉，说是出现了好几天的数据异常，导致他们判断有误，所以客户要求我们赠送一个预警系统（简易），以此来兜住这种类似的问题。项目管理说最好以最小的成本完成该内容，甚至没有前端，纯后端进行数据计算统计、触发预警等。开发周期大概5-7天，由我来主导设计架构。在2024年初的时候，某A外企啤酒公司项目又爆了一个生产问题，本次生产事故是产品缺陷，导致报错影响了主流程判断。是由于客户项目是在好几年前买了的，是1.1.0版本，目前我们的版本已经是1.5.4+了，这个问题已经在新版本修复，但是旧版本没有修复，最终导致本次报错。客户认为该内容属于产品缺陷，我们应在修复产品缺陷时回归同步项目代码，公司侧认为只在新版本进行更新和维护，旧版本出现且新版本已修复问题应按需所求去同步。因此客户发起二期预警的需求，本次二期预警改动量较大，影响第一次的整体内容（由于第一次项目经理要求以最简单的方式开发，没有留太多拓展的口子）某B国际航空公司项目是客户购买的看板趋势产品，要求携带预警功能，该预警功能设计架构与某A外企啤酒公司项目又不一致，且我在有一段时间既参加A有参加B的开发任务，导致思维也比较跳跃。某C会员超市公司项目，目前主管只让我评估了对应的工作量，还没有进行开发，所以目前也没有开始设计。方案设计下面我会讲解两种方案设计，分别是 某A外企啤酒公司 与 某B国际航空公司 项目现在的预警方案设计。他们其中有一些共同点，也有一些不同的底层设计，具体是由于需求所导向。某A外企啤酒公司每天数据量: 25w+数据来源: Elasticsearch需求说明: 支持天类型与小时类型的预警 （天类型：当前天开始至目前时间的数据量 与 过去七天均值进行对比，看环比上升或下降率是否满足预警要求；小时类型：当前小时段与过去七天该小时段均值进行对比，看环比上升或下降率是否满足预警要求）额外补充: 没有前端页面展示，需要手动在某配置中写 Json，仅后端去做数据计算和触发[scode type="green"]说明[/scode]这个项目主要就是两个定时任务来运转整体预警系统的，定时任务分别去做存储和触发两个事情。我们的数据来源基本上是通过读取 Elasticsearch 获取的，在读取数据时已经进行了大量的聚合操作，并在返回结果后再进行一次加工。由于该项目预警需求最小时间片是按照小时进行对比的，所以在读取 Elasticsearch 数据后，可以按照 1 小时分片单位将对应的数据存入 MySQL 中。在触发预警的定时任务中，因为分成天类型与小时类型，我将其分成了两部分，该小时的数据实时的从 Elasticsearch 中读取，该小时之前的数据从 MySQL 中读取，最后将两个数据进行合并以及加工，再进行对比，计算出最终结果。至于为什么分着读取，是由于如果一直实时的去操作 Elasticsearch 可能会对其产生一些影响，且这些数据在上个小时后就已经结束了使命，是一个"死数据"，完全可以存储到 MySQL 当中，为之后预警的其他操作提供一个稳定的数据来源。这种形式也可以加快预警触发的效率，如果在 Elasticsearch 中数据量很大，并且做了极多的 Aggregation 聚合操作，会导致相应慢、节点阻塞等情况。在后续的 MySQL 中，可以定期清理往期数据，保证一个清洁的数据源。由于没有前端的交互，整个流程的触发和操作基本上都是从后端角度去考虑的，包括后续对读取数据行为的优化。整体开发所需的工作量并不是很多，但是一些细节仍需多加考虑。在最终的预警通知中，也应提供高拓展、多可用的方式，或许以后会兼容 邮箱、钉钉、AI电话、短信等方式，对不同的通知人群进行不同的预警方式。其实在这个设计之初我还设计了类似群组用户之类的功能，目前的预警，只要其中一个指标触发了阈值、满足条件后，就会推送给所有配置好的“推送人”，其实这里应该要去分开，不同的预警配置推送给不同的用户，并且应新增一个用户群组，例如运维组、管理组，可以设置全局推送等权限；部分某项目上的人员可以单独对某指标或多个指标进行监控。可惜该功能…

LeetCode | [1276]-Medium 不浪费原料的汉堡制作方案
type: item
date: 2023-12-25T15:20:00+08:00
description: 题目/** * 圣诞活动预热开始啦，汉堡店推出了全新的汉堡套餐。为了避免浪费原料，请你帮他们制定合适的制作计划。 * * 给你两个整数 tomatoSlices 和 cheeseSlices，分别表示番茄片和奶酪片的数目。不同汉堡的原料搭配如下： * * 巨无霸汉堡：4 片番茄和 1 片奶酪 * 小皇堡：2 片番茄和 1 片奶酪 * 请你以 [total_jumbo, total_small]（[巨无霸汉堡总数，小皇堡总数]）的格式返回恰当的制作方案，使得剩下的番茄片 tomatoSlices 和奶酪片 cheeseSlices 的数量都是 0。 * * 如果无法使剩下的番茄片 tomatoSlices 和奶酪片 cheeseSlices 的数量为 0，就请返回 []。 * * * * 示例 1： * * 输入：tomatoSlices = 16, cheeseSlices = 7 * 输出：[1,6] * 解释：制作 1 个巨无霸汉堡和 6 个小皇堡需要 4*1 + 2*6 = 16 片番茄和 1 + 6 = 7 片奶酪。不会剩下原料。 * 示例 2： * * 输入：tomatoSlices = 17, cheeseSlices = 4 * 输出：[] * 解释：只制作小皇堡和巨无霸汉堡无法用光全部原料。 * 示例 3： * * 输入：tomatoSlices = 4, cheeseSlices = 17 * 输出：[] * 解释：制作 1 个巨无霸汉堡会剩下 16 片奶酪，制作 2 个小皇堡会剩下 15 片奶酪。 * 示例 4： * * 输入：tomatoSlices = 0, cheeseSlices = 0 * 输出：[0,0] * 示例 5： * * 输入：tomatoSlices = 2, cheeseSlices = 1 * 输出：[0,1] * * * 提示： * * 0 <= tomatoSlices <= 10^7 * 0 <= cheeseSlices <= 10^7 * * @Author hang.wang * @Date 2023/12/25 15:07 */ public class Class1276 { public static void main(String[] args) { Class1276 clazz = new Class1276(); System.out.println(clazz.numOfBurgers(16, 7)); System.out.println(clazz.numOfBurgers(17, 4)); System.out.println(clazz.numOfBurgers(4, 17)); System.out.println(clazz.numOfBurgers(0, 0)); System.out.println(clazz.numOfBurgers(2, 1)); } public List<Integer> numOfBurgers(int tomatoSlices, int cheeseSlices) { if (tomatoSlices % 2 != 0) { return Collections.emptyList(); } if (cheeseSlices * 2 > tomatoSlices) { return Collections.emptyList(); } if (cheeseSlices * 4 < tomatoSlices) { return Collections.emptyList(); } int small = cheeseSlices; int big = 0; while (small * 2 + big * 4 != tomatoSlices) { if (small == 0) { return Arrays.asList(0, 0); } small--; big++; } return Arrays.asList(big, small); } }解析有点类似鸡兔同笼，但是其中的边界值需要考虑，例如如果给的值为0，返回的结果都为0；如果给的条件不符合（例如奇数），返回空List先按照最小的汉堡消耗进行计算，然后依次减少小汉堡数量，增加大汉堡数量（根据条件已知：奶酪数=大汉堡数+小汉堡数）做该题目时优先考虑把各种情况排除掉奇数时，返回空值当奶酪全做小汉堡时，又超过西红柿数，返回空值当奶酪全做大汉堡时，又小于西红柿数，返回空值当做循环中途，小汉堡数为0时，返回 0, 0当循环结束后，返回对应数值

Elasticsearch | 聚合 (按日统计、根据字段去重等)
type: item
date: 2023-12-08T15:07:00+08:00
description: 前言最近在公司做了一个类似大盘、看板的功能，所有的数据全部存储在ES里面，完全需要自己去ES里面捞数据，并没有一个结构化、过滤好的数据源，完全需要自己对ES的数据进行过滤、筛选、统计，所以就需要用到聚合了，下面是功能图，在图里面其实展示了很多重要信息根据时间范围、其他条件进行筛选数据指标数据以每天为单位进行展示统计所有数据，变成总量数据这里就需要考验到对ES聚合的使用了，因为不可能查询每个指标，都访问一遍ES，不仅指标多，指标也要根据日期进行分桶，不可能每次都发一次请求，即便是多线程，那也对ES压力太大了，使用了过多的网络资源来满足需求，况且时间范围越多，请求次数也越多，所以很不合理这个时候就体现出来聚合的重要性了，ES底层自己支持大量的聚合方式，可以满足按天为单位聚合数据，展示每天数据量有多少，也可以在其上层进行过滤，展示某个数据每天有多少 （不得不说，这简直太棒了）底层的指标都是我来计算提供数据的，所以对这块很熟，也使用了策略设计模式去做这个内容，满足其拓展性聚合是什么首先要明白，聚合是什么聚合分析是数据库中重要的功能特性，完成对一个查询的数据集中数据的聚合计算，如：找出某字段（或计算表达式的结果）的最大值、最小值，计算和、平均值等。ES作为搜索引擎兼数据库，同样提供了强大的聚合分析能力。对一个数据集求最大、最小、和、平均值等指标的聚合，在ES中称为指标聚合(metric)而关系型数据库中除了有聚合函数外，还可以对查询出的数据进行分组group by，再在组上进行指标聚合。在ES中group by称为分桶，桶聚合bucketing通过聚合，我们可以统计出绝大部分的数据，也可以查出来自己想要的指标，满足其使用性用法按天聚合数据{ "aggs": { "daily_hits_true": { "filter": { "bool": { "must": [ { "range": { "statDate": { "gte": 1700668800000, "lt": 1701359999000 } } }, { "nested": { "path": "searchFields", "query": { "bool": { "must": [ { "term": { "searchFields.name": "C_S_TDRESULT" } }, { "term": { "searchFields.value.raw": "true" } } ] } } } } ] } }, "aggs": { "daily_hits": { "date_histogram": { "field": "statDate", "interval": "1d", "format": "yyyy-MM-dd" } } } } } }上面的聚合就是一个非常简单的例子，我根据时间范围(statDate)进行筛选数据，判断C_S_TDRESULT是否为true进行过滤最后添加一个 daily_hits 聚合条件统计出每天的数据这里的聚合使用的是ES的 date_histogram，他可以按照指定时间范围的内容进行统计，例如几小时，也是完全可以的field: 根据哪个字段进行统计interval: 时间周期是多少，1d就是按照一天为单位统计数据format: 展示出来的时间格式是什么，如果按照1小时统计，需要填写小时的单位，需求是按照天统计，所以不关注时分秒的内容去重聚合去重聚合顾名思义，假如你数据存在一个 PHONE_NUMBER 字段第1条数据: PHONE_NUMBER = qwe第2条数据: PHONE_NUMBER = abc第3条数据: PHONE_NUMBER = qwe最终去重聚合统计出来的结果是2，这个2的数据就是聚合后的结果{ "aggs": { "daily_hits": { "date_histogram": { "field": "statDate", "interval": "1d", "format": "yyyy-MM-dd" }, "aggregations": { "searchFields": { "nested": { "path": "searchFields" }, "aggregations": { "relativeInAcNumFilter": { "filter": { "bool": { "must": [ { "term": { "searchFields.name": "S_S_MOBINO" } } ] } }, "aggregations": { "relativeInAcNum": { "cardinality": { "field": "searchFields.value.raw" } } }…

LeetCode | [20]-Easy 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效
type: item
date: 2023-12-08T15:03:00+08:00
description: 题目/** * @Author hang.wang * @Date 2023/11/24 10:22 * * 给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串 s ，判断字符串是否有效。 * * 有效字符串需满足： * * 左括号必须用相同类型的右括号闭合。 * 左括号必须以正确的顺序闭合。 * 每个右括号都有一个对应的相同类型的左括号。 * * * 示例 1： * 输入：s = "()" * 输出：true * * 示例 2： * 输入：s = "()[]{}" * 输出：true * * 示例 3： * 输入：s = "(]" * 输出：false * * 提示： * 1 <= s.length <= 104 * s 仅由括号 '()[]{}' 组成 */代码public class Class20 { public static void main(String[] args) { Class20 class20 = new Class20(); System.out.println(class20.isValid("([)]")); System.out.println(class20.isValid("()")); System.out.println(class20.isValid("()[]{}")); System.out.println(class20.isValid("(]")); } public char exchange(char c) { switch (c) { case '(': return ')'; case ')': return '('; case '[': return ']'; case ']': return '['; case '{': return '}'; case '}': return '{'; } return ' '; } public boolean isValid(String s) { if (s.length() % 2 != 0) { return false; } if (s.length() == 1) { if (isPost(s.charAt(0))) { return false; } } Deque<Character> queue = new ArrayDeque<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (isPost(c)) { try { Character remove = queue.removeFirst(); if (exchange(c) != remove) { return false; } } catch (Exception e) { return false; } } else { queue.addFirst(c); } } return queue.isEmpty(); } public boolean isPost(char c) { switch (c) { case ')': case ']': case '}': return true; } return false; } }解析利用堆栈的特性，如果判断是前括号，就存进去，如果是后括号，就取出来一个值，然后判断这俩是否匹配

LeetCode | [14]-Easy 编写一个函数来查找字符串数组中的最长公共前缀
type: item
date: 2023-12-08T14:56:00+08:00
description: 代码/** * @Author hang.wang * @Date 2023/11/24 13:38 * 编写一个函数来查找字符串数组中的最长公共前缀。 * 如果不存在公共前缀，返回空字符串 ""。 * 示例 1： * 输入：strs = ["flower","flow","flight"] * 输出："fl" * * 示例 2： * 输入：strs = ["dog","racecar","car"] * 输出："" * 解释：输入不存在公共前缀。 * * 提示： * 1 <= strs.length <= 200 * 0 <= strs[i].length <= 200 * strs[i] 仅由小写英文字母组成 */ public class Class14 { public static void main(String[] args) { Class14 class14 = new Class14(); System.out.println(class14.longestCommonPrefix(new String[]{"flower", "flow", "flight"})); System.out.println(class14.longestCommonPrefix(new String[]{"dog", "racecar", "car"})); } public String longestCommonPrefix(String[] strs) { List<String> collect = Arrays.stream(strs).sorted(Comparator.comparing(String::length)).collect(Collectors.toList()); String smallest = collect.get(0); int index = smallest.length(); for (int i = 0; i < smallest.length(); i++) { String substring = smallest.substring(0, index); AtomicBoolean flag = new AtomicBoolean(true); collect.forEach(str -> { if (str.indexOf(substring) != 0) { flag.set(false); } }); if (flag.get()) { return substring; } if (index == 1) { return ""; } index--; } return ""; } }思路取List中最短长度的值，以此判断满不满足，节约资源，当不满足后，长度-1，把末尾的字符去掉，一直到变成空字符串

LeetCode | [13]-Easy 罗马数字转换成阿拉伯数字
type: item
date: 2023-12-08T14:33:00+08:00
description: 示例/** * 示例 1: * * 输入: s = "III" * 输出: 3 * 示例 2: * * 输入: s = "IV" * 输出: 4 * 示例 3: * * 输入: s = "IX" * 输出: 9 * 示例 4: * * 输入: s = "LVIII" * 输出: 58 * 解释: L = 50, V= 5, III = 3. * 示例 5: * * 输入: s = "MCMXCIV" * 输出: 1994 * 解释: M = 1000, CM = 900, XC = 90, IV = 4. * @param args */代码public static void main(String[] args) { Class13 class13 = new Class13(); System.out.println(class13.romanToInt("III")); System.out.println(class13.romanToInt("IV")); System.out.println(class13.romanToInt("IX")); System.out.println(class13.romanToInt("LVIII")); System.out.println(class13.romanToInt("MCMXCIV")); } public int romanToInt(String s) { Map<String, Integer> map = new HashMap<>(); map.put("I", 1); map.put("V", 5); map.put("X", 10); map.put("L", 50); map.put("C", 100); map.put("D", 500); map.put("M", 1000); int total = 0; List<Integer> list = new ArrayList<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); String s1 = String.valueOf(c); Integer tempValue = map.get(s1); list.add(tempValue); if (i + 1 < s.length()) { char tempChar = s.charAt(i + 1); String s2 = String.valueOf(tempChar); if (map.get(s2) > tempValue) { total -= tempValue; } else { total += tempValue; } } else { total += tempValue; } } System.out.println(list); return total; }解析罗马数字主要特点ABA若A小于B，那么数值是B-A+A，根据位置的不同计算不同的数值，需要判断数字在哪个位置，与下一个数字进行比较可以先把所有罗马字转换成数值，然后再进行对比、计算