• 华为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代码

    1. import java.util.Collection;
    2. import java.util.HashMap;
    3. import java.util.HashSet;
    4. import java.util.Iterator;
    5. import java.util.Map;
    6. import java.util.Scanner;
    7. import java.util.Set;
    8. /**
    9. * 字符串化繁为简
    10. *
    11. * @since 2023.09.13
    12. * @version 0.2
    13. * @author Frank
    14. */
    15. public class StringSimplify {
    16. public static void main(String[] args) {
    17. Scanner sc = new Scanner(System.in);
    18. while (sc.hasNext()) {
    19. String sourceStr = sc.nextLine();
    20. processStringSimplify(sourceStr);
    21. }
    22. }
    23. private static void processStringSimplify(String sourceStr) {
    24. StringBuffer sb = new StringBuffer();
    25. // 第一次遍历 sourceStr
    26. int i = 0;
    27. Map> charSetMap = new HashMap>();
    28. while (i < sourceStr.length()) {
    29. int leftPT = sourceStr.indexOf('(', i);
    30. int rightPT = sourceStr.indexOf(')', i);
    31. // 没有小括号了
    32. if (leftPT == -1 && rightPT == -1) {
    33. sb.append(sourceStr.substring(i));
    34. break;
    35. }
    36. // 把 i 和 左括号之间部分加到sb。
    37. sb.append(sourceStr.substring(i, leftPT));
    38. String setStr = sourceStr.substring(leftPT + 1, rightPT);
    39. if (setStr.length() > 0) {
    40. // 构建 charSetMap
    41. constructCharSetMap(charSetMap, setStr);
    42. }
    43. i = rightPT + 1;
    44. }
    45. Map, Character> setTarCharMap = new HashMap, Character>();
    46. constructSetTarCharMap(charSetMap, setTarCharMap);
    47. Map charSrcTarMap = new HashMap();
    48. constructCharSrcTarMap(charSetMap, setTarCharMap, charSrcTarMap);
    49. String ret = getReplacedString(charSrcTarMap, sb.toString());
    50. if (ret.length() == 0) {
    51. ret = "0";
    52. }
    53. System.out.println(ret);
    54. }
    55. private static String getReplacedString(Map charSrcTarMap, String tmpStr) {
    56. StringBuffer sb = new StringBuffer();
    57. for (int i = 0; i < tmpStr.length(); i++) {
    58. Character curChar = tmpStr.charAt(i);
    59. Character tarChar = charSrcTarMap.get(curChar);
    60. if (tarChar != null) {
    61. sb.append(tarChar);
    62. } else {
    63. sb.append(curChar);
    64. }
    65. }
    66. return sb.toString();
    67. }
    68. private static void constructCharSrcTarMap(Map> map,
    69. Map, Character> setTarCharMap, Map charSrcTarMap) {
    70. map.forEach((key, value) -> {
    71. Set tmpValue = map.get(key);
    72. Character finalValue = setTarCharMap.get(tmpValue);
    73. charSrcTarMap.put(key, finalValue);
    74. });
    75. }
    76. private static Set getValueByIgnoreKey(Map> map, Character key) {
    77. Set ret = map.get(key);
    78. if (ret != null) {
    79. return ret;
    80. }
    81. Character newKey;
    82. if (Character.isLowerCase(key)) {
    83. newKey = Character.toUpperCase(key);
    84. } else {
    85. newKey = Character.toLowerCase(key);
    86. }
    87. return map.get(newKey);
    88. }
    89. private static void constructSetTarCharMap(Map> map,
    90. Map, Character> setTarCharMap) {
    91. Collection> allSets = map.values();
    92. for (Iterator> iter = allSets.iterator(); iter.hasNext();) {
    93. Set curSet = iter.next();
    94. Character firstChar = getSetFirstChar(curSet);
    95. setTarCharMap.put(curSet, firstChar);
    96. }
    97. }
    98. private static Character getSetFirstChar(Set curSet) {
    99. Character ret = null;
    100. for (Iterator iter = curSet.iterator(); iter.hasNext();) {
    101. Character curChar = iter.next();
    102. if (ret == null) {
    103. ret = curChar;
    104. } else if (curChar < ret) {
    105. ret = curChar;
    106. }
    107. }
    108. return ret;
    109. }
    110. private static void constructCharSetMap(Map> map, String setStr) {
    111. int inMapCnt = 0;
    112. Set set1 = null;
    113. for (int i = 0; i < setStr.length(); i++) {
    114. Character curChar = setStr.charAt(i);
    115. if (getValueByIgnoreKey(map, curChar) != null) {
    116. inMapCnt++;
    117. if (inMapCnt == 1) {
    118. set1 = getValueByIgnoreKey(map, curChar);
    119. }
    120. }
    121. }
    122. if (inMapCnt == 0 || inMapCnt == 1) {
    123. // 全都不在map中,则新建一个 set,并放到map中。
    124. // inMapCnt == 1, 放到初始化的 set1中。
    125. if (inMapCnt == 0) {
    126. set1 = new HashSet();
    127. }
    128. for (int i = 0; i < setStr.length(); i++) {
    129. Character curChar = setStr.charAt(i);
    130. set1.add(curChar);
    131. map.put(curChar, set1);
    132. }
    133. return;
    134. }
    135. // inMapCnt >= 2的情况,所有的值都合并到 set1 中。
    136. for (int i = 0; i < setStr.length(); i++) {
    137. Character curChar = setStr.charAt(i);
    138. set1.add(curChar);
    139. Set set2 = getValueByIgnoreKey(map, curChar);
    140. if (set2 == null) {
    141. map.put(curChar, set1);
    142. continue;
    143. } else if (set1 == set2) {
    144. continue;
    145. }
    146. // 把 set2 合并到 set1 中
    147. for (Iterator iter = set2.iterator(); iter.hasNext();) {
    148. Character charInSet2 = iter.next();
    149. set1.add(charInSet2);
    150. map.put(charInSet2, set1);
    151. }
    152. }
    153. }
    154. }

    这题的逻辑处理并不复杂,但代码量有些多,近 170 行,耗费了整整 1 个小时。

    JavaScript代码

    1. const rl = require("readline").createInterface({ input: process.stdin });
    2. var iter = rl[Symbol.asyncIterator]();
    3. const readline = async () => (await iter.next()).value;
    4. void async function() {
    5. while (line = await readline()) {
    6. processStringSimplify(line);
    7. }
    8. }();
    9. function processStringSimplify(sourceStr) {
    10. var output = "";
    11. // 第一次遍历 sourceStr
    12. var i = 0;
    13. var charSetMap = new Map();
    14. while (i < sourceStr.length) {
    15. var leftPT = sourceStr.indexOf('(', i);
    16. var rightPT = sourceStr.indexOf(')', i);
    17. // 没有小括号了
    18. if (leftPT == -1 && rightPT == -1) {
    19. output += sourceStr.substring(i);
    20. break;
    21. }
    22. // 把 i 和 左括号之间部分加到sb。
    23. output += sourceStr.substring(i, leftPT);
    24. var setStr = sourceStr.substring(leftPT + 1, rightPT);
    25. // console.log("setStr: " + setStr);
    26. if (setStr.length > 0) {
    27. // 构建 charSetMap
    28. constructCharSetMap(charSetMap, setStr);
    29. }
    30. i = rightPT + 1;
    31. }
    32. var setTarCharMap = new Map();
    33. constructSetTarCharMap(charSetMap, setTarCharMap);
    34. var charSrcTarMap = new Map();
    35. constructCharSrcTarMap(charSetMap, setTarCharMap, charSrcTarMap);
    36. var ret = getReplacedString(charSrcTarMap, output);
    37. if (ret.length == 0) {
    38. ret = "0";
    39. }
    40. console.log(ret);
    41. }
    42. function getReplacedString(charSrcTarMap, tmpStr) {
    43. var sb = "";
    44. for (var i = 0; i < tmpStr.length; i++) {
    45. var curChar = tmpStr.charAt(i);
    46. var tarChar = charSrcTarMap.get(curChar);
    47. if (tarChar != null) {
    48. sb += tarChar;
    49. } else {
    50. sb += curChar;
    51. }
    52. }
    53. return sb;
    54. }
    55. function constructCharSrcTarMap(map, setTarCharMap, charSrcTarMap) {
    56. map.forEach(function(value, key, thisMap) {
    57. var finalValue = setTarCharMap.get(value);
    58. charSrcTarMap.set(key, finalValue);
    59. });
    60. }
    61. function getValueByIgnoreKey(map, key) {
    62. var ret = map.get(key);
    63. if (ret != null) {
    64. return ret;
    65. }
    66. var newKeyCode;
    67. if (key >= 'a' && key <= 'z') {
    68. // 小写字母转换成大写
    69. newKeyCode = 'A'.charCodeAt() + (key.charCodeAt() - 'a'.charCodeAt());
    70. } else {
    71. newKeyCode = 'a'.charCodeAt() + (key.charCodeAt() - 'A'.charCodeAt());
    72. }
    73. return map.get(String.fromCharCode(newKeyCode));
    74. }
    75. function constructSetTarCharMap(map, setTarCharMap) {
    76. map.forEach(function(item) {
    77. var firstChar = getSetFirstChar(item);
    78. setTarCharMap.set(item, firstChar);
    79. });
    80. }
    81. function getSetFirstChar(curSet) {
    82. var ret = null;
    83. curSet.forEach(function(curChar) {
    84. if (ret == null) {
    85. ret = curChar;
    86. } else if (curChar < ret) {
    87. ret = curChar;
    88. }
    89. });
    90. return ret;
    91. }
    92. function constructCharSetMap(map, setStr) {
    93. var inMapCnt = 0;
    94. var set1 = null;
    95. for (var i = 0; i < setStr.length; i++) {
    96. var curChar = setStr.charAt(i);
    97. if (getValueByIgnoreKey(map, curChar) != null) {
    98. inMapCnt++;
    99. if (inMapCnt == 1) {
    100. set1 = getValueByIgnoreKey(map, curChar);
    101. }
    102. }
    103. }
    104. if (inMapCnt == 0 || inMapCnt == 1) {
    105. // 全都不在map中,则新建一个 set,并放到map中。
    106. // inMapCnt == 1, 放到初始化的 set1中。
    107. if (inMapCnt == 0) {
    108. set1 = new Set();
    109. }
    110. for (var i = 0; i < setStr.length; i++) {
    111. var curChar = setStr.charAt(i);
    112. set1.add(curChar);
    113. map.set(curChar, set1);
    114. }
    115. return;
    116. }
    117. // inMapCnt >= 2的情况,所有的值都合并到 set1 中。
    118. for (var i = 0; i < setStr.length; i++) {
    119. var curChar = setStr.charAt(i);
    120. set1.add(curChar);
    121. var set2 = getValueByIgnoreKey(map, curChar);
    122. if (set2 == null) {
    123. map.set(curChar, set1);
    124. continue;
    125. } else if (set1 == set2) {
    126. continue;
    127. }
    128. set2.forEach(function(value) {
    129. set1.add(value);
    130. map.set(value, set1);
    131. })
    132. }
    133. }

    (完)

  • 相关阅读:
    PMP子过程定义总结
    Springboot 过滤器、拦截器、全局异常处理
    打造无证服务化:这个政务服务平台有点不一样
    Java-字符编码-roadmap
    基于Python简单实现接口自动化测试(详解)
    【SpringBoot】秒杀业务:redis+拦截器+自定义注解+验证码简单实现限流
    Hi3861 OpenHarmony嵌入式应用入门--hello world
    随记 | 我的 CSDN 两周年创作纪念日
    云栖大会72小时沉浸式精彩回顾
    zabbix监控添加监控项及其监控Mysql、nginx
  • 原文地址:https://blog.csdn.net/ZiJinShi/article/details/132692630