华为OD机考算法题:字符串化繁为简

目录

题目部分

解读与分析

代码实现


题目部分

题目字符串化繁为简
题目说明给定一个输入字符串,字符串只可能由英文字母 ('a'~'z'、'A'~'Z' )和左右小括号 ('('、')')组成。当字符里存在小括号时,小括号是成对的,可以有一个或多个小括号对,小括号对不会嵌套,小括号对内可以包含1个或多个英文字母,也可以不包含英文字母。当小括号对内包含多个英文字母时,这些字母之间是相互等效的关系,而且等效关系可以在不同的小括号对之间传递,即当存在'a'和'b'等效和存在'b'和'c' 等效时,'a' 和 'c' 也等效,另外,同一个英文字母的大写字母和小写字母也相互等效 (即使它们分布在不同的括号对里)。
需要对这个输入字符串做简化,输出一个新的字符串,输出字符串里只需保留输入字符串里的没有被小括号对包含的字符(按照输入字符串里的字符顺序) ,并将每个字符替换为在小括号对里包含且字典序最小的等效字符。
如果简化后的字符串为空,请输出为"0"。
 
示例:
输入字符串为"never(dont)give(run)up(f)()",初始等效字符集合为('d','o','n','t')、('r','u','n'),由于等效关系可以传递,因此最终等效字符集合为('d','o','n','t','r','u'),将输入字符串里的剩余部分按字典序最小的等效字符替换后得到"devedgivedp"。
输入描述input_string
输入为 1 行,代表输入字符串。
输出描述output_string
输出为 1 行,代表输出字符串。
补充说明输入字符串的长度在 1 ~ 100000 之间。
-----------------------------------------------------------
示例
示例1
输入()abd
输出abd
说明输入字符串里没有被小括号包含的子字符串为"abd",其中每个字符没有等效字符,输出为"abd"。
示例2
输入(abd)demand(fb)for
输出aemanaaor
说明等效字符集为('a','b','d','f'),输入字符串里没有被小括号包含的子字符串集合为 "demandfor",将其中字符替换为字典序最小的等效字符后输出为:"aemanaaor"。
示例3
输入()happy(xyz)new(wxy)year(t)
输出happwnewwear
说明等效字符集为('x','y','z','w'),输入字符串里没有被小括号包含的子字符串集合为"happynewyear",将其中字符替换为字典序最小的等效字符后输出为:"happwnewwear"。
示例4
输入()abcdefgAC(a)(Ab)(C)
输出AAcdefgAC
说明等效字符集为('a','A','b'),输入字符里没有被小括号包含的子字符串集合为"abcdefgAC",将其中字符替换为字典序最小的等效字符后输出为:"AAcdefgAC"。


解读与分析

题目解读

本题的输入和输出都是一段字符串。输出字符串在输入字符串的基础上进行精简,精简按要求如下:

1. 忽略输入字符串中所有使用小括号包裹起来的子字符串;
例如,如果输入字符串为 abc(xxx)def(xxxx),那么先去掉括号的部分,把字符串转换成中间字符串 abcdef,再进行后续操作。
2. 对于所有小括号对中所包含的字符,构建等效字符集(构建等效字符集的规则参见“分析与思路”部分)。一个字符集中所有的字符等效某个字母,这个字母是字符集中字典序最小的那个字母。
例如,某个有效字符集是 {'A', 'a', 'b'},在这个字符集的 3 个字母中,字典序最小的字母是 'A',所以当碰到这 3 个字母中任意一个时,一律转换成 'A'。

需要注意两点:
1. 输入字符串中的等效字符集可能有多个。
2. 相同英文字母的大写和小写视作等效字符。

分析与思路

设源输入的字符串为 sourceStr,接下来字符串精简可以分两步实现:

1.  遍历 sourceStr,在遍历过程中,进行如下操作。
 ①  去掉 sourceStr 中括号对包含的部分,生成中间字符串 tmpStr。
 ② 
构建等效字符集(具体算法稍后说明),并算出每个字符集等效的字母(即等效字符集中字典序最小的那个字母)。创建一个map,命名为 charSetMap,在遍历 sourceStr 过程中用于建立字符(key)和其所在的字符集(value)之间的关系。

2. 遍历完 sourceStr,构建好字符集后,进行如下操作。
 ①  创建一个 map,命名为 setTarCharMap,用于存储字符集(key)和其等效的字母(value)之间的关系。  
 ②  创建一个 map,命名为 charSrcTarMap,在 charSetMap 和 setTarCharMap 基础上,建立需要转换的字符(key)和最终转换的字符(value)之间的关系。  

 3. 遍历中间字符串 tmpStr,设最终字符串为 tarStr。遍历字符时,判断此字符是否在charSrcTarMap中存在key,如存在则替换成这个 key 所对应的 value,加到 tarStr 的末尾,否则原封不动地加到 tarStr 末尾。

最终, tarStr 的结果为精简后的字符串。

-----------------------------------------------------------

在以上的实现中,第 1 步中的关键步骤 构建等效字符集 写得比较粗略,下面详解构建等效字符集的步骤:

1. 创建一个 map,命名为 charSetMap,其 key 为等效字符集中的字符,value 为字符 set,用于存储 key 所在的结合。
举例说明,如果存在 2 个等效字符集{'a', 'b', 'c'},{'d', 'e'},那么 charSetMap 有 5 个 key,分别为 'a'、'b'、'c'、'd'、'e'。其中,'a'、'b'、'c' 这 3 个 key 所对应的 value 为 set{'a', 'b', 'c'},另外 2 个 key 对应的 value 为 set {'d','e'}。

2. 遍历字符串 srcStr,每碰到一个括号对时,提取出括号对中的所有字符,放到数组 tmpCharArr 中。遍历 tmpCharArr,检查 tmpCharArr 中的字符是否是 charSetMap 的key, 如果
 ①  tmpCharArr 中所有的字符都不是 charSetMap 的 key,则意味着 tmpCharArr 中的所有字符与已存在的等效字符集不存在交集,那么构建一个 set,设为 tmpSet,tmpSet 中包含 tmpCharArr 的所有字符。遍历 tmpCharArr,把 tmpCharArr 的元素作为 key 添加到 charSetMap,其对应的 value 为刚创建的 tmpSet。
 ② tmpCharArr 中至少有一个字符是 charSetMap 的 key,此时可能存在等效字符集合并的情况。
 · 遍历 tmpCharArr 时,当碰到第一个字符(假设为 tmpChar)是 charSetMap 的 key 时,设 set1 为 charSetMap.get( tmpChar )。
 · 继续遍历 tmpCharArr,如果下一个字符(假设为 char2)不是 charSetMap 的 key,那么把 char2 添加到 set1 中,并把 ( char2, set1) 添加到 charSetMap 中。
 · 继续遍历 tmpCharArr,如果下一个字符(假设为 char3)是 charSetMap 的 key,并且 charSetMap.get( char3) == set1,则意味着 char3 已经在 set1 中,跳过。
 · 继续遍历 tmpCharArr,如果下一个字符(假设为 char4)是 charSetMap 的 key,并且 charSetMap.get( char4) != set1,设 set2 = charSetMap.get( char4 ),则需要把 set2 合并到 set1 中。把 set2 中所有的元素添加到 set1 中,并把 set2 中所有字符作为 key 添加到 charSetMap 中,它们对应的 value 为 set2。

遍历完字符串 srcStr 之后,等效字符集构建完毕。

此算法需要遍历 2 次字符串,其时间复杂度为 o(n),由于字母个数有限,创建的各种 map 包含的元素都很有限,额外的辅助空间与字符串长度无关,空间复杂度为 o(1)。 


代码实现

Java代码

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;/*** 字符串化繁为简* * @since 2023.09.06* @version 0.1* @author Frank*/
public class StringSimplify {public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 第一行输入一串数字,以空格分隔String sourceStr = sc.nextLine();StringBuffer sb = new StringBuffer();// 第一次遍历 sourceStrint i = 0;Map<Character, Set<Character>> charSetMap = new HashMap<Character, Set<Character>>();while (i < sourceStr.length()) {int leftPT = sourceStr.indexOf('(', i);int rightPT = sourceStr.indexOf(')', i);// 没有小括号了if (leftPT == -1 && rightPT == -1) {sb.append(sourceStr.substring(i));break;}// 把 i 和 左括号之间部分加到sb。sb.append(sourceStr.substring(i, leftPT));String setStr = sourceStr.substring(leftPT + 1, rightPT);if (setStr.length() > 0) {// 构建 charSetMapconstructCharSetMap(charSetMap, setStr);}i = rightPT + 1;}Map<Set<Character>, Character> setTarCharMap = new HashMap<Set<Character>, Character>();constructSetTarCharMap(charSetMap, setTarCharMap);Map<Character, Character> charSrcTarMap = new HashMap<Character, Character>();constructCharSrcTarMap(charSetMap, setTarCharMap, charSrcTarMap);String ret = getReplacedString(charSrcTarMap, sb.toString());if( ret.length() == 0 ){ret = "0";}System.out.println(ret);}private static String getReplacedString(Map<Character, Character> charSrcTarMap, String tmpStr) {StringBuffer sb = new StringBuffer();for (int i = 0; i < tmpStr.length(); i++) {Character curChar = tmpStr.charAt(i);Character tarChar = charSrcTarMap.get(curChar);if (tarChar != null) {sb.append(tarChar);} else {sb.append(curChar);}}return sb.toString();}private static void constructCharSrcTarMap(Map<Character, Set<Character>> map,Map<Set<Character>, Character> setTarCharMap, Map<Character, Character> charSrcTarMap) {map.forEach((key, value) -> {Set<Character> tmpValue = map.get(key);Character finalValue = setTarCharMap.get(tmpValue);charSrcTarMap.put(key, finalValue);});}private static Set<Character> getValueByIgnoreKey(Map<Character, Set<Character>> map, Character key) {Set<Character> ret = map.get(key);if (ret != null) {return ret;}Character newKey;if (Character.isLowerCase(key)) {newKey = Character.toUpperCase(key);} else {newKey = Character.toLowerCase(key);}return map.get(newKey);}private static void constructSetTarCharMap(Map<Character, Set<Character>> map,Map<Set<Character>, Character> setTarCharMap) {Collection<Set<Character>> allSets = map.values();for (Iterator<Set<Character>> iter = allSets.iterator(); iter.hasNext();) {Set<Character> curSet = iter.next();Character firstChar = getSetFirstChar(curSet);setTarCharMap.put(curSet, firstChar);}}private static Character getSetFirstChar(Set<Character> curSet) {Character ret = null;for (Iterator<Character> iter = curSet.iterator(); iter.hasNext();) {Character curChar = iter.next();if (ret == null) {ret = curChar;} else if (curChar < ret) {ret = curChar;}}return ret;}private static void constructCharSetMap(Map<Character, Set<Character>> map, String setStr) {int inMapCnt = 0;Set<Character> set1 = null;for (int i = 0; i < setStr.length(); i++) {Character curChar = setStr.charAt(i);if (getValueByIgnoreKey(map, curChar) != null) {inMapCnt++;if (inMapCnt == 1) {set1 = getValueByIgnoreKey(map, curChar);}}}if (inMapCnt == 0 || inMapCnt == 1) {// 全都不在map中,则新建一个 set,并放到map中。// inMapCnt == 1, 放到初始化的 set1中。if (inMapCnt == 0) {set1 = new HashSet<Character>();}for (int i = 0; i < setStr.length(); i++) {Character curChar = setStr.charAt(i);set1.add(curChar);map.put(curChar, set1);}return;}// inMapCnt >= 2的情况,所有的值都合并到 set1 中。for (int i = 0; i < setStr.length(); i++) {Character curChar = setStr.charAt(i);set1.add(curChar);Set<Character> set2 = getValueByIgnoreKey(map, curChar);if (set2 == null) {map.put(curChar, set1);continue;} else if (set1 == set2) {continue;}// 把 set2 合并到 set1 中for (Iterator<Character> iter = set2.iterator(); iter.hasNext();) {Character charInSet2 = iter.next();set1.add(charInSet2);map.put(charInSet2, set1);}}}
}

这题的逻辑处理并不复杂,但代码量有些多,近 170 行,耗费了整整 1 个小时。如果是在考试中,我未必有足够的时间完成它。

JavaScript代码

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function() {let input = [];while (line = await readline()) {input.push(line);}var sourceStr = input[0];var output = "";// 第一次遍历 sourceStrvar i = 0;var charSetMap = new Map();while (i < sourceStr.length) {var leftPT = sourceStr.indexOf('(', i);var rightPT = sourceStr.indexOf(')', i);// 没有小括号了if (leftPT == -1 && rightPT == -1) {output += sourceStr.substring(i);break;}// 把 i 和 左括号之间部分加到sb。output += sourceStr.substring(i, leftPT);var setStr = sourceStr.substring(leftPT + 1, rightPT);if (setStr.length > 0) {// 构建 charSetMapconstructCharSetMap(charSetMap, setStr);}i = rightPT + 1;}var setTarCharMap = new Map();constructSetTarCharMap(charSetMap, setTarCharMap);var charSrcTarMap = new Map();constructCharSrcTarMap(charSetMap, setTarCharMap, charSrcTarMap);var ret = getReplacedString(charSrcTarMap, output);if( ret.length == 0 ){ret = "0";}console.log(ret);function getReplacedString(charSrcTarMap, tmpStr) {var sb = "";for (var i = 0; i < tmpStr.length; i++) {var curChar = tmpStr.charAt(i);var tarChar = charSrcTarMap.get(curChar);if (tarChar != null) {sb += tarChar;} else {sb += curChar;}}return sb;}function constructCharSrcTarMap(map, setTarCharMap, charSrcTarMap) {map.forEach(function(value, key, thisMap) {var finalValue = setTarCharMap.get(value);charSrcTarMap.set(key, finalValue);});}function getValueByIgnoreKey(map, key) {var ret = map.get(key);if (ret != null) {return ret;}var newKeyCode;if (key >= 'a' && key <= 'z') {// 小写字母转换成大写newKeyCode = 'A'.charCodeAt() + (key.charCodeAt() - 'a'.charCodeAt());} else {newKeyCode = 'a'.charCodeAt() + (key.charCodeAt() - 'A'.charCodeAt());}return map.get(String.fromCharCode(newKeyCode));}function constructSetTarCharMap(map, setTarCharMap) {map.forEach(function(item) {var firstChar = getSetFirstChar(item);setTarCharMap.set(item, firstChar);});}function getSetFirstChar(curSet) {var ret = null;curSet.forEach(function(curChar) {if (ret == null) {ret = curChar;} else if (curChar < ret) {ret = curChar;}});return ret;}function constructCharSetMap(map, setStr) {var inMapCnt = 0;var set1 = null;for (var i = 0; i < setStr.length; i++) {var curChar = setStr.charAt(i);if (getValueByIgnoreKey(map, curChar) != null) {inMapCnt++;if (inMapCnt == 1) {set1 = getValueByIgnoreKey(map, curChar);}}}if (inMapCnt == 0 || inMapCnt == 1) {// 全都不在map中,则新建一个 set,并放到map中。// inMapCnt == 1, 放到初始化的 set1中。if (inMapCnt == 0) {set1 = new Set();}for (var i = 0; i < setStr.length; i++) {var curChar = setStr.charAt(i);set1.add(curChar);map.set(curChar, set1);}return;}// inMapCnt >= 2的情况,所有的值都合并到 set1 中。for (var i = 0; i < setStr.length; i++) {var curChar = setStr.charAt(i);set1.add(curChar);var set2 = getValueByIgnoreKey(map, curChar);if (set2 == null) {map.set(curChar, set1);continue;} else if (set1 == set2) {continue;}set2.forEach(function(value) {set1.add(value);map.set(value, set1);})}}}();

(完)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“内存炸弹”DDOS拒绝服务攻击

Windows平台演示 最早的内存炸弹是 zip 炸弹&#xff0c;也称为死亡 zip&#xff0c;它是一种恶意计算机文件&#xff0c;旨在使读取该文件的程序崩溃或瘫痪。zip 炸弹不会劫持程序的操作&#xff0c;而是利用解压缩压缩文件所需的时间、磁盘空间或内存。 zip 炸弹的一个示例…

mvvm框架下对wpf的DataGrid多选,右键操作

第一步&#xff1a;在DataGrid中添加ContextMenu <DataGrid.ContextMenu><ContextMenu><MenuItem Header"删除选中项" Command"{Binding DeleteSelectedCommand}" /></ContextMenu></DataGrid.ContextMenu> 第二步&#xff…

ldconfig和ldd用法

ldconfig和ldd用法 一、ldconfig ldconfig是一个动态链接库管理命令&#xff0c;为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig。 ldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出…

关于 RK3568的linux系统killed用户应用进程(用户现象为崩溃) 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/132710642 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

基于Matlab实现多个数字水印案例(附上源码+数据集)

数字水印是一种在数字图像或视频中嵌入特定信息的技术&#xff0c;以保护知识产权和防止盗版。在本文中&#xff0c;我们将介绍如何使用Matlab实现数字水印。 文章目录 实现步骤源码数据集下载 实现步骤 首先&#xff0c;我们需要选择一个用于嵌入水印的图像。这可以是原始图像…

Linux权限的概念和管理

Linux权限的概念和管理 1. Linux权限的概念2. Linux权限管理2.1 文件访问者的分类&#xff08;人&#xff09;2.2 文件类型和访问权限&#xff08;事物属性&#xff09;2.2.1 文件类型2.2.2 基本权限 2.3 文件权限值的表示方法2.4文件访问权限的相关设置方法1. chmod&#xff0…

ESP32用作经典蓝牙串口透传模块与手机进行串口通信

ESP32用作经典蓝牙串口透传模块与手机进行串口通信 简介ESP32开发板Arduino程序手机与ESP32开发板进行蓝牙串口透传通信总结 简介 ESP32-WROOM-32模组集成了双模蓝牙包括传统蓝牙&#xff08;BR/EDR&#xff09;、低功耗蓝牙&#xff08;BLE&#xff09;和 Wi-Fi&#xff0c;具…

linux修改最大线程数却未生效的原因

可能是没有重新对新文件进行编译 更改一个进程所能创建的最大进程数之前 更改一个进程所能创建的最大进程数之后 测试代码 #include <iostream> #include <unistd.h> #include <sys/wait.h> #include <string.h> #include <stdio.h> #include…

HTTP【总结】

1. 当用户在浏览器输入网址回车之后&#xff0c;网络协议都做了哪些工作&#xff1f; 首先解析出URL中的域名&#xff0c;根据域名获取对应的ip地址&#xff0c;从浏览器缓存中查看&#xff0c;如果没有则从本机域名解析文件hosts中查看&#xff0c;还没有则从DNS的层层解析。…

数据库相关基础知识

第一章 概念 1、数据&#xff1a;描述事物的符号记录称为数据。特点&#xff1a;数据和关于数据的解释不可分。 2、数据库&#xff1a;长期存储在计算机内、有组织、可共享的大量的数据的集合。数据库中的数据按照一定的数据模型组织、描述和存储&#xff0c;具有较小的冗余度、…

思维导图怎么变成ppt?4个思维导图一键生成ppt的方法

做好的思维导图如何变成一份ppt&#xff1f;本文罗列了4个可行方法&#xff0c;一起来看看吧。 一 直接复制粘贴 这是最简单的方法&#xff0c;虽然这样可能会花费一些时间&#xff0c;但可以确保内容排版和布局与你想要的一致。当然&#xff0c;我们大可使用更高效的方法。…

编程要搞明白的东西(二)

文章目录 一、简介二、面向对象编程基础2.1 面向对象编程概述2.2 类和对象2.2.1 类的定义和特点2.2.2 对象的创建和使用 2.3 封装、继承与多态的关系2.3.1 封装的概念和优势2.3.2 继承的概念和作用2.3.3 多态的概念和实现方式 三、封装3.1 封装的定义和原则3.2 封装的实现方法3…

VIC模型教程

详情点击链接&#xff1a;VIC模型教程 一&#xff1a;VIC模型的原理与特点 1.VIC模型各模块的主要原理 2.VIC模型的特点及优势 3.VIC模型的适用范围及其限制 4.VIC模型主要输入和输出文件解析案例一 &#xff1a;基于QGIS的VIC模型建模 1.Windows版本VIC模型安装及问题解…

Pycharm创建项目时如何自动添加头部信息

1.打开PyCharm&#xff0c;选择File--Settings 2.依次选择Editor---Code Style-- File and Code Templates---Python Script 3..添加头部内容 可以根据需要添加相应的信息 #!/usr/bin/python3可用的预定义文件模板变量为&#xff1a;$ {PROJECT_NAME} - 当前项目的名称。$ {NAM…

解决Maven依赖下载问题:从阿里云公共仓库入手

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

用递归实现字符串逆序(不使用库函数)

文章目录 前言一、题目要求二、解题步骤1.大概框架2.如何反向排列&#xff1f;3.模拟实现strlen4.实现反向排列5.递归实现反向排列 总结 前言 嗨&#xff0c;亲爱的读者们&#xff01;我是艾老虎尤&#xff0c;今天&#xff0c;我们将探索一个题目&#xff0c;这个题目对新手非…

Python---元组

元组特点 1. 可以容纳多个数据 2. 可以容纳不同类型的数据 (混装) 3. 数据是有序存储的 (下标索引) 4. 允许重复数据存在 5. 不可以修改 (增加或删除元素等) 【可以修改内部list的内部元素---见页尾】 6. 支持for和while循环 定义元组 # 定义元组 变量 (元素, 元素2, 元素…

数据库服务器是什么意思?数据库服务器有哪些?

数据库服务器是什么意思?现在市场上有很多的服务器的类型&#xff0c;比如数据库服务器&#xff0c;但是很多人对数据库服务器是什么意思?数据库服务器有哪些并不是很熟悉&#xff0c;那么&#xff0c;聚名企服为您详解一下。 一&#xff1a;数据库服务器是什么意思 数据库服…

Java中xml转javaBean

Java中xml转javaBean maven坐标 <dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.13.4</version></dependency>代码测试 import cn.hutool.js…

开发板插入sd/tf卡后自动挂载

测试平台-hisi-dv500 要在Linux系统上实现TF卡&#xff08;Micro SD卡&#xff09;插入后自动挂载&#xff0c;类似于SD卡/TF卡插入也会触发内核事件,你可以使用udev工具来监控并处理这些事件,创建一个udev规则文件来捕获TF卡插入事件. 1:创建一个udev规则文件&#xff0c;例…