【问题标题】:How to count characters in order in a String?如何按顺序计算字符串中的字符?
【发布时间】:2018-05-06 14:00:09
【问题描述】:

我有一个String 用于 2D 平台游戏中的编辑器,其中用户单击 16x60 矩形板以创建自定义板。我遍历这些矩形并得到一个我想修剪的字符串。 x 是块,a 是空白空间。我需要这个来绘制关卡。

String s = "x1x1x1x1a1a1x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1";

我想将其修剪为x4a2x1a54。基本上是把出现的 x 和 a 加起来。

我该怎么做?

【问题讨论】:

  • 遍历字符,记住你读的最后一个字母和当前计数;如果当前字母与最后一个字母相同,则增加计数,否则输出最后一个字母和计数,并重置两者。

标签: java string javafx char stringbuilder


【解决方案1】:

遍历字符并存储最后一个字母字符。对于这些字符中的每一个,解析它后面的数字。如果字母字符与最后一个字符相同,您可以将此数字添加到计数中,否则将旧结果写入输出:

public static String compress(String input) {
    if (input == null || input.isEmpty()) {
        return input;
    }
    char type = ' ';
    int count = 0;
    StringBuilder output = new StringBuilder();
    int index = 0;
    final int length = input.length();
    while (index < length) {
        char elementType = input.charAt(index);

        // parse number
        int elementCount = 0;
        char c;
        for (index++; index < length && Character.isDigit(c = input.charAt(index)); index++) {
            elementCount = 10 * elementCount + (c - '0');
        }

        if (elementType == type) {
            count += elementCount;
        } else {
            // finish counting last type
            output.append(type).append(count);
            type = elementType;
            count = elementCount;
        }
    }
    output.delete(0, 2); // remove substring added for the initial count/type
    output.append(type).append(count); // append last entry
    return output.toString();
}

这允许您输出与1 不同的数字,例如

compress("a22a20x9")

【讨论】:

    【解决方案2】:

    只是让您知道,stackoverflow 不是获取代码的论坛,而是帮助您编写代码并指出错误或提供建议。

    我不太喜欢需求,但由于这个问题让我有点困扰,这里是我的版本。不幸的是,我不能只使用 Streams 并且必须向其中添加一个 Arraylist(也许使用流的 reduce 和 collect 方法可以在一行中)但它可以按预期工作。

        String s = "x1x1x1x1a1a1x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1";
        ArrayList<String[]> map = new ArrayList<>();
        for(String c:s.split("(?<=\\G..)")){
            if(map.isEmpty()||!map.get(map.size()-1)[0].equals(c.substring(0,1))){
                map.add(new String[]{c.substring(0,1),c.substring(1)});
            } else{
                map.get(map.size()-1)[1]=(Integer.parseInt(map.get(map.size()-1)[1])+Integer.parseInt(c.substring(1)))+"";
            }
        }
        StringBuilder sb = new StringBuilder();
        map.forEach(x-> sb.append(x[0]).append(x[1]));
        System.out.println(sb.toString());
    

    编辑:我更改了代码,以便将 1 以外的其他数字也添加到字符串中,我现在没有使用 Streams,而是使用 for-loops

    【讨论】:

    • 您可能还应该注意此代码中所做的假设,因为规范不是很清楚,例如你假设输入字符串只有1 作为值,等等。
    • 只是为了使用流,您 a) 创建了一个原本不需要的列表 b) 使用了一个不适合存储您想要存储在其中的两种类型的数组,并且以字符串格式存储Integers,从字符串中解析它们并将它们转换回字符串只是为了增加它们......
    • 我使用流来超越 forloop,我更改了代码以便没有流和@James_D 我添加了您的建议,谢谢
    【解决方案3】:

    我建议的第一件事是将这个字符串分解成更易于管理的部分,如下所示:

        String s = "x1x1x1x1a1a1x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1";
        String[] splitS = s.split("1");
        //Contents of the array are: [x, x, x, x, a, a, x, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a]
        //We can now loop through each string in the string array and count how many we have of what
        ArrayList<String> values = new ArrayList<String>();
        int start = 0, end = 0, grandCounter = 0;
        String current = splitS[0];
        for(int i = 1; i < splitS.length-1; i++){
            if(!splitS[i].equals("x") && current.equals("x")){
                end = i;
                values.add("x" + (end - start));
                grandCounter += (end-start);
                start = end;
                end = 0;
                current = "a";
            } else if(!splitS[i].equals("a") && current.equals("a")){
                end = i;
                values.add("a" + (end - start));
                start = end;
                end = 0;
                current = "x";
                grandCounter += (end-start);
            }
        }
        values.add( "a" + (splitS.length-grandCounter) + "");
        System.out.println(values.toString().replace("[", "").replace(",", "").replace("]", "").replace(" ", ""));
    

    上面的代码在控制台打印时准确返回以下内容:x4a2x1a58

    如果您对您的工作有任何其他疑问或问题,请告诉我!

    【讨论】:

    • 很多这似乎是多余的。例如。 String current = "x" ; if (splitS[0].equals("a")) { current ="a" ; }:为什么不直接做String current = splitS[0];continue 语句也是多余的。
    • 我写得很快,所以我为冗余道歉,我现在就去解决这个问题。
    • 另外,如果我没看错的话,你认为最后一个是a?这似乎是一个不合理的假设。
    • 没错,我确实有,但如果最后一个不是 a 那么你最后只有一个 a0 所以如果 OP 不希望他可以删除它与另一个 .replace
    • "你最后只有一个 a0"。真的吗?为什么?
    【解决方案4】:

    这里有一个比已经发布的更简单的方法:

     public static String customTrim(String s){
    
    String res = "";
    String last = "";
    int count = 0;
    
    for (int i = 0; i < s.length() - 1; i+=2){
    
      String curr = "" + s.charAt(i) + s.charAt(i+1);
    
      if(!curr.equals(last)){
          count = 1;
          res += "" + s.charAt(i) + count;
      }
    
      else{
        int subLength = ("" + count).length();
        count++;
        res = res.substring(0, res.length()- subLength) + count;
      }
    
      last = curr;
    
    }
    return res;
    }
    

    【讨论】:

    • 永远不要在循环中通过串联来构建字符串。
    • @James_D 为什么不呢?
    • 因为性能太差了。在字符串的次修改中,您创建一个新的字符串对象并将旧字符串中的字符复制到新字符串中。您基本上会执行 O(n^2) 字符副本,其中 n 是结果字符串的长度。请参阅stackoverflow.com/q/5234147(例如)
    【解决方案5】:

    您可以按照 fabian 发布的方式进行操作,效果很好。但是,我自己通过三个简单的 if 就知道了。

        StringBuilder sb = new StringBuilder();
    
        List<String> values = new ArrayList<>();
    
        String s = "x1x1x1x1a1a1x1x1a1a1a1";
        String[] splitString = s.split("1");
        // "xxxxaaxxaaa"
        // to x4a2x2a3
    
        String current = splitString[0];
        int occ = 0;
    
        for(int i = 0; i < splitString.length;i++){
    
            if(!splitString[i].equals(current)){
                values.add(current+occ);
                occ = 0;
                current = splitString[i];
            }
    
            if(splitString[i].equals(current)){
                occ++;
            }
    
            if(i == splitString.length-1){
                values.add(current+occ);
            }
    
        }
    
        for (String str: values
             ) {
            sb.append(str);
        }
    
        System.out.println(sb);
    }
    

    【讨论】:

    • 这会做出各种不包括在实际问题中的假设。
    • 什么意思?如果你有不同的角色,比如我计划在我的游戏中实现的 f 或 g,这仍然有效。
    • 您假设所有整数都是1,这在问题中没有说明。如果这是真的,那么一开始就将它们放在那里似乎是多余的。
    • 哦,是的。你是对的。它应该已被删除。
    猜你喜欢
    • 1970-01-01
    • 2022-12-21
    • 2021-02-22
    • 2022-06-13
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 1970-01-01
    • 2019-09-14
    相关资源
    最近更新 更多