为了赶周一hr上班,昨天凌晨完成了西安华为的OD机试。总共三道题,这里总结记录下其中一道难度一的题目。
题目要求
现已知对指定字符串存在类似加密算法,“ddd”可加密为“3d”,“aaaabbb”可加密为“4a3b”,“aabbb”可加密为“aa3b”。 也就是说,某一字符若连续重复3次及3次以上,可压缩为“重复次数+重复的字符”,其他情况则不压缩。 现需要实现相反的解密过程,如给定输入“3d”,则输出“ddd”,输入“3b10c”,则输出“bbbcccccccccc”。 若输入“aa”,则输出“aa”,若输入“2a”,则为错误加密,则输出“!error”。“11”也为错误加密,输出“!error”。 输入‘0-9’及‘a-z’之外的其他字符均为错误。 程序需遵循,标准oj输入输出。
解题思路
如果直接对格式校验再解密则会多一遍循环,所以这里采用对给定字符串直接遍历,一边遍历一边校验并解密,每次遍历都可能会将解密的内容以StringBuilder的方式追加。
遍历的过程:分三种情况,当前为字符为数字(0-9),小写字母(a-z),或其他字符。如果当前位置为数字,则需要记录下来,下次遍历的时候,如果下一位是字母,则就可以解密(数字<3除外),如果下一位为数字,就需要将两位数字转成真正的数值,多位数字同理。遍历时,还需要记录字符以及此次解密字符,便于之后比较,因为可能出现“数字+字母”后又是该字母的情况、“数字+字母”后又是同样的“数字+字母”组合,这种是不合法的,同样3次及3次以上同字母连续也是不合法的。
注意:在char与int转换时,并不是真正想要的数值,因为它是ASCII码的转换,所以转换时这里直接减去了‘a’。在char类型的数值比较时,也直接用char(‘0’-‘9’)比较的。
代码实现
这里代码以java实现,其他语言同理。同时,因为线上判题是采用oj,但这里为了便于本地测试,提前将case计入文件中,直接读取文件判断就行。代码中线上oj和本地case用例均有,取消对应注释即可。若本地case文件方式,请修改对应文件路径。
解密算法,可修改,重新实现。main方法无需变动。
/** * 题目要求: * 现已知对指定字符串存在类似加密算法,“ddd”可加密为“3d”,“aaaabbb”可加密为“4a3b”,“aabbb”可加密为“aa3b”。 * 也就是说,某一字符若连续重复3次及3次以上,可压缩为“重复次数+重复的字符”,其他情况则不压缩。 * 现需要实现相反的解密过程,如给定输入“3d”,则输出“ddd”,输入“3b10c”,则输出“bbbcccccccccc”。 * 若输入“aa”,则输出“aa”,若输入“2a”,则为错误加密,则输出“!error”。“11”也为错误加密,输出“!error”。 * 输入‘0-9’及‘a-z’之外的其他字符均为错误。 * 程序遵循,标准oj输入输出 * * @author humh * @date 2020/6/7 */ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Scanner; public class Main { /** * 解密字符串算法 * * @author humh * @date 2020/6/8 * @param input 需要解密的字符串 * @return java.lang.String 解密后的未加密字符串 */ public static String decodeStr(String input) { char[] chars = input.toCharArray(); // 记录重复字符的重复次数(追加结果后,要先清空0,才能下次再遍历)-1为默认清空值(默认取-1,是因为字符中可能有0) int count = -1; // 存放上一次遍历的字符,可能是重复字符,也可能不是,根据count值判断(遍历如果是数字的话,先清空!) '!'为默认值 char charFirst = '!'; // 字母重复次数 0为默认值 int sameCount = 0; // 最后一次解密追加的字符串 char decodeCharLast = '!'; // 最终解密的结果 StringBuilder result = new StringBuilder(); for (int i = 0; i < chars.length; i++) { //重复字符解密追加至结果中 if (count != -1 && charFirst != '!') { if (count < 3 || decodeCharLast == charFirst) { // 重复次数2的加密情况,如2a,或者最后一次解密与当前解密为同一字符,如3a3a3b,均不合法 return "!error"; } for (int j = 0; j < count; j++) { result.append(charFirst); } sameCount = 2; decodeCharLast = charFirst; count = -1; } char charTemp = chars[i]; if ('a' <= charTemp && charTemp <= 'z') { // 前位是数字,且未进行解密追加的情况 if (count != -1) { if (charFirst != charTemp) { charFirst = charTemp; sameCount = 1; } else { return "!error"; } } else { if (charFirst == charTemp) { if (sameCount == 2) { // 连续重复第三次,如:aaa;或者加密后又重复,如:3aa return "!error"; } else { // 字符第二次出现 ++sameCount; result.append(charTemp); } } else { // 字符第一位出现(a)、与上位字符不同(ab)或加密后字符不同(3ab) charFirst = charTemp; sameCount = 1; // 这里需要记录为解密追加字符,便于下次对比,如bb3b的情况 decodeCharLast = charTemp; result.append(charTemp); } } } else if ('0' <= charTemp && charTemp <= '9') { if (count != -1) { // 上一个字符为数字,当前位字符仍为数字,如:12a count = count * 10 + charTemp - '0'; } else { // 前面没有数字,清空原先字符位,才能进行之后的重复解密追加,同时不需要再对之前的字符进行比较 charFirst = '!'; count = charTemp - '0'; sameCount = 0; } } else { // 非法情况,不是‘a-z’,或者‘1-9’ return "!error"; } } // 解决最后一次遍历,无法解密追加问题 if (count != -1 && charFirst != '!') { if (count < 3 || decodeCharLast == charFirst) { return "!error"; } for (int j = 0; j < count; j++) { result.append(charFirst); } count = -1; } // 最后一位为数字的情况 if (count != -1) { return "!error"; } return result.toString(); } public static void main (String[] args) { // 1 oj输入 // Scanner scan = new Scanner(System.in); // while (scan.hasNextLine()) { // System.out.println(decodeStr(scan.nextLine())); // } // scan.close(); //2 本地文件输入测试 Scanner scan = null; try { scan = new Scanner(new FileInputStream("D:decode_case.txt")); } catch (FileNotFoundException e) { e.printStackTrace(); } String numQ; while (scan.hasNextLine()) { numQ = scan.nextLine(); if (!scan.nextLine().equals(decodeStr(numQ.substring(3)))) { System.out.println(String.format("%s 处的case未通过测试", numQ.substring(0, 2))); } } System.out.println("已跑完所有case"); } }
本地case文件:
注意,旧代码昨日机试,线上case 85%, 上述代码为修改后的最新代码, 可能会有线上oj case不通过的情况,但目前还未发现。如有问题,请指正。
发表评论