【问题标题】:Efficient Java language constructs to check if string is pangram?有效的 Java 语言构造来检查字符串是否是 pangram?
【发布时间】:2016-10-19 05:12:23
【问题描述】:

到目前为止,我已经想出了这个。我试图最小化字符串操作并隔离内置数据类型、数组和整数操作的解决方案。

我正在寻找更优雅的方式来检查 java 中的 pangram 字符串。

优雅,代码行数最少,也欢迎其他高效算法。

请提供不带 lambda 表达式的建议。

    private static boolean isPangrams(String ip) {

        char[] characterArray = ip.toLowerCase().toCharArray();
        int map[] = new int[26];
        int sum = 0;

        for(char current : characterArray) {

            int asciiCode = (int) current;
            if (asciiCode >= 97 && asciiCode <= 122) {

                if (map[122 - asciiCode] == 0) {

                    sum += 1;
                    map[122 - asciiCode] = 1;
                }
            }
        }

        return sum == 26;
    }

【问题讨论】:

  • 好吧,“改进”工作代码应该转到 codereview.stackexchange.com ......无论如何:它并没有变得更好,但你可以使用 Bitset 而不是 int 数组 - 为什么当你真正需要真/假时使用数字?!
  • BitSet ,是一个简洁的输入。谢谢。

标签: java algorithm performance optimization pangram


【解决方案1】:

您可以使用按位运算:

private static boolean isPangrams(String ip) {
    int flags = 0;
    for(char current : ip.toLowerCase().toCharArray()) {
        if (current >= 'a' && current <= 'z') {
            flags |= 0x01<<(current-'a');
        }
    }
    return flags == 0x3ffffff;
}

jDoodle

代码的工作方式如下:我们考虑一个 32 位数字的 int。最多 26 位的每一位都是一个标志(可以说是boolean)。最初所有标志都是false,因为我们用0 初始化flags

现在我们遍历字符串的字符。如果字符是小写字母,我们将对应flag的flag设置为true(不管之前是否设置为true)。

最后我们检查最低 26 位是否都设置为true。如果是,flags 等于 0x3ffffff(这是一个十六进制数,等于 1111111111111111111111 二进制。如果是,我们返回 true。否则,我们返回 false

通常按位运算比if 语句和布尔值快,所以我希望这个程序会快很多。

【讨论】:

  • 为什么要转换 char 'current' 并将其存储在 int 'asciiCode' 中?你做的所有操作都可以用原来的char'current'而不是'asciiCode'来完成。
【解决方案2】:

如果你想要一个难以理解的几行答案:

private static boolean isPangrams(String ip) {
  return 26== (new HashSet(Arrays.asList(ip.toUpperCase().replaceAll("[^A-Z]", "").toCharArray()))).size();
}

说明:

  1. 将字符串设为大写(将“a”和“A”处理为相同)
  2. 删除所有字符,不是 A、B ... Z
  3. 将其转换为char[]
  4. 将数组转换为Collection
  5. 将集合添加到 Set 以消除所有双胞胎
  6. 测试集合的大小。

您应该意识到这段代码不易阅读且性能不佳。

【讨论】:

  • 是的,性能部分失败。不过,符合我的要求。
【解决方案3】:

如果字符串包含 int 变量中的给定字母,您可以“打包”数据。

static boolean pangram (String s) {
    int check = 0;
    String lowerCase = s.toLowerCase();
    for (int i = 0; i < lowerCase.length(); i++) {
      char ch = lowerCase.charAt(i);
      if (ch >= 'a' && ch <= 'z') {
        check |= (1 << s.charAt(i) - 'a');
      }
    }
    return check == 67108863;
  }

最后的幻数是0b00000011111111111111111111111111

【讨论】:

    【解决方案4】:

    最有效的解决O(n)时间复杂度:

    1. 遍历字符串并将每个字母放入HashMap (key: letter, value: count)
    2. 遍历地图并检查字母表中的每个字母

    【讨论】:

    • 问题中的算法也是O(n)。因此,复杂性不可能得到改善。
    【解决方案5】:

    如果你发现你可以用 return false 语句停止整个方法 map[122 - asciiCode] 不等于零,因为从那时起它就不再是 pangram 了,你省去了 for() 的其余部分——我说的对吗? 我知道这不是您所期望的改进(尤其是只有 26 个步骤),而只是我想到的一些事情

            if (map[122 - asciiCode] == 0) {
    
                sum += 1;
                map[122 - asciiCode] = 1;
            } else return false;
    

    【讨论】:

    • 我认为 pangram 的定义是所有个字符(a-z)至少一次。
    • 你是对的,一个 pangram 必须有每个字符至少一次。我想到了一个“完美的 pangram”,其中每个字母只包含一次。 Wikipedia/Pangram:“一个完美的 pangram 只包含字母表中的每个字母一次,可以被认为是字母表的变位词”
    猜你喜欢
    • 2014-09-06
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 2020-05-27
    • 2019-05-05
    • 2019-10-04
    • 2020-03-25
    相关资源
    最近更新 更多