【问题标题】:Count characters ocurrence in string in a perfomant way?‽?以高效的方式计算字符串中出现的字符?‽?
【发布时间】:2020-05-05 13:58:10
【问题描述】:

我正在做以下编程练习:Numericals of a String。声明是:

给你一个输入字符串。

对于字符串中的每个符号,如果它是第一个出现的字符, 将其替换为“1”,否则将其替换为您的次数 已经看过了...

但是您的代码会性能足够吗?例子:

input   =  "Hello, World!"
result  =  "1112111121311"

input   =  "aaaaaaaaaaaa"
result  =  "123456789101112"

字符串中可能有一些非ASCII字符。

注意:不会有 int 域溢出(字符出现将 少于 20 亿)。

我已经写了以下答案:

import java.util.*;
import java.util.stream.*;
public class JomoPipi {
  public static String numericals(String s) {
    System.out.println("s: "+s);
    Map<String, Long> ocurrences = Arrays.stream(s.split("")).
                                    collect(Collectors.groupingBy(c -> c,
                                    Collectors.counting()));
    System.out.println("ocurrences: "+ocurrences.toString());                                    
    StringBuilder result = new StringBuilder();                                    
    for(int i = s.length()-1; i >= 0; i--){
      String c = String.valueOf(s.charAt(i));
      result.append(ocurrences.get(c) + " ");
      ocurrences.put(c, ocurrences.get(c)-1);
    }
    System.out.println("result: "+result.toString());
    String[] chars = result.toString().split(" ");
    Collections.reverse(Arrays.asList(chars));
    String sorted = String.join("",chars);
    System.out.println("sorted: "+sorted);
    return sorted;
  }
}

但是,当输入字符串很大时,它会超时(执行时间在 16000 毫秒以上)。

要查看它是如何工作的,有一个带有非常小的输入字符串的跟踪:

s: Hello, World!
result: 1 1 3 1 2 1 1 1 1 2 1 1 1 
sorted: 1112111121311

此外,我还写了以下替代答案:

import java.util.*;
import java.util.stream.*;
public class JomoPipi {
  public static String numericals(String s) {
    System.out.println("s: "+s);
    Map<String, Long> ocurrences = Arrays.stream(s.split("")).
                                    collect(Collectors.groupingBy(c -> c,
                                    Collectors.counting()));
    String[] result = new String[s.length()];                                   
    for(int i = s.length()-1; i >= 0; i--){
      String c = String.valueOf(s.charAt(i));
      result[i] = String.valueOf(ocurrences.get(c));
      ocurrences.put(c, ocurrences.get(c)-1);
    }
    System.out.println("result: "+Arrays.toString(result));
    return String.join("",result);
  }
}

即便如此,它仍然超时。

这是一个带有小输入字符串的跟踪:

s: Hello, World!
result: [1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 3, 1, 1]

我们如何改进解决方案?哪种算法可以更好地处理非常大的输入字符串?为了改进这个答案,我们应该调试和避免的瓶颈在哪里?

为了尝试自己解决,我已阅读:

编辑:这里我们有一个基于@khelwood 建议的答案:

import java.util.*;
import java.util.stream.*;
public class JomoPipi {
  public static String numericals/*????->????*/(String s) {
    Map<String, Integer> ocurrences = new HashMap<String,Integer>();
    StringBuilder result = new StringBuilder();
    for(int i = 0; i < s.length(); i++){
      String c = String.valueOf(s.charAt(i));
      ocurrences.putIfAbsent(c, 0);
      ocurrences.put(c,ocurrences.get(c)+1);
      result.append(ocurrences.get(c));
    }
    return result.toString();
  }
}

【问题讨论】:

  • 从说明看来,您应该能够只遍历字符串一次,同时保持字符数。通过首先计算字符然后向后遍历字符串,您已经使它变得更加困难。

标签: java string algorithm performance char


【解决方案1】:

我认为您的Map 是正确的,但您的密钥类型应该是Character,而您的计数类型应该是Integer。我认为当您对结果进行反转和排序时,您出错了。此外,如果没有流,您的代码会更容易阅读(编写快速代码的关键部分是 write dumb code)。例如,

public static String numericals(String s) {
    int len = s.length();
    Map<Character, Integer> occurrences = new HashMap<>(); // ocurrences
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < len; i++) {
        char ch = s.charAt(i);
        int count = occurrences.getOrDefault(ch, 0) + 1;
        occurrences.put(ch, count);
        sb.append(count);
    }
    return sb.toString();
}

然后进行测试

public static void main(String[] args) {
    String[] input = { "Hello, World!", "aaaaaaaaaaaa" };
    String[] output = { "1112111121311", "123456789101112" };
    for (int i = 0; i < input.length; i++) {
        String result = numericals(input[i]);
        System.out.printf("%s %b%n", result, result.equals(output[i]));
    }
}

哪些输出

1112111121311 true
123456789101112 true

【讨论】:

  • 我想知道int[Character.MAX_VALUE] 是否会加快速度,但是是的,这不需要微优化,甚至不确定它是否会起作用。 (1/4 MB,所以没有那么多内存)
  • 如果你想要性能,你会使用char[26] 来计算字符而不是HashMap。不需要 HashMap 查找。
  • @Charles 字符串中可能有一些非ascii字符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 2011-04-29
  • 2014-04-24
  • 2011-12-16
相关资源
最近更新 更多