华为OD (E卷,200分)-字母组合过滤组合字符串(Java)

题目描述

每个数字关联多个字母,关联关系如下:

  • 0 关联 “a”,”b”,”c”
  • 1 关联 “d”,”e”,”f”
  • 2 关联 “g”,”h”,”i”
  • 3 关联 “j”,”k”,”l”
  • 4 关联 “m”,”n”,”o”
  • 5 关联 “p”,”q”,”r”
  • 6 关联 “s”,”t”
  • 7 关联 “u”,”v”
  • 8 关联 “w”,”x”
  • 9 关联 “y”,”z”

输入一串数字后,通过数字和字母的对应关系可以得到多个字母字符串(要求按照数字的顺序组合字母字符串);

屏蔽字符串:屏蔽字符串中的所有字母不能同时在输出的字符串出现,如屏蔽字符串是abc,则要求字符串中不能同时出现a,b,c,但是允许同时出现a,b或a,c或b,c等;

给定一个数字字符串和一个屏蔽字符串,输出所有可能的字符组合;

例如输入数字字符串78和屏蔽字符串ux,输出结果为uw,vw,vx;数字字符串78,可以得到如下字符串uw,ux,vw,vx;由于ux是屏蔽字符串,因此排除ux,最终的输出是uw,vw,vx;

输入描述

第一行输入为一串数字字符串,数字字符串中的数字不允许重复,数字字符串的长度大于0,小于等于5;

第二行输入是屏蔽字符串,屏蔽字符串的长度一定小于数字字符串的长度,屏蔽字符串中字符不会重复;

输出描述

输出可能的字符串组合

注:字符串之间使用逗号隔开,最后一个字符串后携带逗号

示例1

输入

78
ux

输出

uw,vw,vx,

    说明

    ux完全包含屏蔽字符串ux,因此剔除

    示例2

    输入

    78
    x
    

    输出

    uw,vw,
    

      错误的官方题解

      import java.util.HashSet;
      import java.util.Scanner;
      
      public class Main {
          static String[] map = {"abc", "def", "ghi", "jkl", "mno", "pqr", "st", "uv", "wx", "yz"};
      
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
      
              // 输入数字字符串
              char[] digits = sc.next().toCharArray();
              // 输入屏蔽字符串
              String filter = sc.next();
      
              // 根据数字字符串得到每个数字对应的字母字符串
              String[] letters = new String[digits.length];
              for (int i = 0; i < digits.length; i++) {
                  letters[i] = map[digits[i] - '0'];
              }
      
              // 用于存储结果的字符串
              StringBuilder sb = new StringBuilder();
              // 开始进行深度优先搜索
              dfs(letters, 0, new StringBuilder(), sb, filter, new HashSet<>());
      
              // 输出结果
              System.out.println(sb.toString());
          }
      
          public static void dfs(
                  String[] letters, int index, StringBuilder path, StringBuilder res, String filter, HashSet<Character> used) {
              if (index == letters.length) {
                  // 过滤包含屏蔽字符串的路径
                  if (!path.toString().contains(filter)) {
                      res.append(path).append(",");
                  }
                  return;
              }
      
              // 对于每个数字,遍历其对应的字母字符串
              for (int i = 0; i < letters[index].length(); i++) {
                  char c = letters[index].charAt(i);
                  if (!used.contains(c)) {
                      path.append(c);
                      used.add(c);
                      dfs(letters, index + 1, path, res, filter, used);
                      path.deleteCharAt(path.length() - 1);
                      used.remove(c);
                  }
              }
          }
      }
      

      这个题解是错的,题目中这句话要求字符串中不能同时出现a,b,c,但是允许同时出现a,b或a,c或b,c等,明显要求的是不出现这些字符,但是题解却是根据字符串来检查的,于是有如下情况:

      ux都是屏蔽字符里的,但却被视为合法的输出

      个人题解

      import java.util.HashMap;
      import java.util.Map;
      import java.util.Scanner;
      
      
      public class 字母组合过滤组合字符串 {
      
          static Map<Integer , char[]> map = new HashMap<>();
          static {
              map.put(0 , new char[]{'a','b','c'});
              map.put(1 , new char[]{'d','e','f'});
              map.put(2 , new char[]{'g','h','i'});
              map.put(3 , new char[]{'j','k','l'});
              map.put(4 , new char[]{'m','n','o'});
              map.put(5 , new char[]{'p','q','r'});
              map.put(6 , new char[]{'s','t'});
              map.put(7 , new char[]{'u','v'});
              map.put(8 , new char[]{'w','x'});
              map.put(9 , new char[]{'y','z'});
          }
      
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              String numStr = sc.nextLine();
              String delStr = sc.nextLine();
      
              char[] delCh = delStr.toCharArray();
              int mark = 0;
              for (int i = 0; i < delCh.length; i++) {
                  mark += 1 << i;
              }
      
              StringBuilder ans = new StringBuilder();
              dfs(numStr , delCh , "" , 0 , mark , ans);
      
              System.out.println(ans);
      
          }
      
          public static void dfs(String numStr , char[] delCh , String word , int i, int mark , StringBuilder ans){
              if (i >= numStr.length()){
                  ans.append(word).append(",");
                  return;
              }
              int num = Integer.parseInt(numStr.substring(i, i + 1));
              char[] chars = map.get(num);
              for (int j = 0 ; j < chars.length ; j ++){
                  char c = chars[j];
                  int markIndex = -1; //记录屏蔽字符的标记位,用于回溯
                  for (int k = 0; k < delCh.length; k++) {
                      //属于屏蔽字符
                      if (delCh[k] == c){ 
                          mark -= 1 << k; //此屏蔽字符标记位设置为0
                          markIndex = k;
                          break;
                      }
                  }
                  if (markIndex >= 0 && mark == 0){ //完全匹配屏蔽字符,当前字符不可选用
                      mark += 1 << markIndex;//回滚当前标记位
                      continue;
                  }
      
                  dfs(numStr , delCh , word + c, i + 1 , mark , ans); //对下一个数字进行取用字符
                  
                  //如果当前是屏蔽字符,进行回溯
                  if (markIndex >= 0){
                      mark += 1 << markIndex; //回溯
                  }
      
              }
      
      
          }
      }

      对于顺序问题进行了解决,输出情况如下:

      这道华为OD机试题要求使用Java实现过滤组合字符串。题目大意是给定若干个字符串,其中只包含小写字母,要求从中选出若干个字符串组合成一个新的字符串,使得新字符串中每个字母至少出现两次,同时新字符串长度最小。如果存在多种组合方式,则输出字典序最小的一种。 对于这道题,我们可以先对输入的字符串进行处理,将每个字符串中的重复字母去除,然后计算每个字符出现的次数。在此基础上,我们可以使用回溯算法,依次尝试从所有字符串中选取不同的组合,每次选取完都要判断当前组合是否符合要求,并更新最小字典序的组合。 在回溯算法的实现中,我们可以使用递归函数实现。递归函数中需要传递的参数包括当前已选取的字符串组合),已选取字符的个数和每个字符剩余可用次数。在递归函数中,我们首先需要判断当前组合是否符合要求,如果符合则进行字典序比较并更新最小字典序组合,然后继续尝试选取下一个字符串加入组合。如果组合不符合要求,则直接退出递归。 需要注意的是,在回溯算法的实现过程中,我们需要使用一个HashSet保存之前已经选取过的字符串,以避免重复运算。 总之,这是一道较为综合性的算法题,需要掌握回溯算法的思想以及Java的基本语法和数据结构。同时,对于本题的实现可能还需要花费一定的时间来调试和优化代码。
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值