Java 字符串数组只包含字谜?

Java 字符串数组只包含字谜?,java,algorithm,hashmap,anagram,Java,Algorithm,Hashmap,Anagram,我做了一个关于字谜的练习,看起来很容易,我怀疑我遗漏了什么。 我实现的解决方案就是我将很快介绍的解决方案,我想问您是否能想到我的解决方案的任何优化、方法的改变或问题。 我用Java实现了这个算法 现在,练习。 作为输入,我有一个文本,作为输出,我应该返回该文本的每一行是否是另一行的字谜。 也就是说,对于输入: 一辆出租汽车使最烦躁的小鱼们哈哈大笑 最烦人的小鲤鱼哈哈大笑 计程车契据洗牌百万不会 一份出租汽车契据在百万个镇上洗牌 程序应该返回True。 输入: 一辆出租汽车使最烦躁的小鱼们哈哈大笑

我做了一个关于字谜的练习,看起来很容易,我怀疑我遗漏了什么。 我实现的解决方案就是我将很快介绍的解决方案,我想问您是否能想到我的解决方案的任何优化、方法的改变或问题。 我用Java实现了这个算法

现在,练习。 作为输入,我有一个文本,作为输出,我应该返回该文本的每一行是否是另一行的字谜。 也就是说,对于输入:

一辆出租汽车使最烦躁的小鱼们哈哈大笑
最烦人的小鲤鱼哈哈大笑
计程车契据洗牌百万不会
一份出租汽车契据在百万个镇上洗牌

程序应该返回True。 输入:

一辆出租汽车使最烦躁的小鱼们哈哈大笑
一条怒气冲冲的小鱼哈哈大笑
计程车契据洗牌百万不会
一份出租汽车契据在百万个镇上洗牌

输出必须为False(当然是因为第二行)

现在,我想的很简单:

  • 我创建了两个HashMap:ref和cur
  • 我解析文本的第一行,填充ref。我只计算字母顺序
  • 对于另一行,我将该行解析为cur并检查cur.equals(ref):如果是,则返回false
  • 如果我到达文本的末尾,这意味着每一行都是另一行的一个字谜,所以我返回true
而且……就是这样。 我用88000行的输入文本进行了尝试,效果非常快

有什么评论吗?建议?优化


非常感谢您的帮助。

假设您的HashMap是来自(字符)->(字符串中出现的次数)的映射,您几乎拥有了它

我假设您应该忽略空格和标点符号,并将大写字母和小写字母视为相同。如果您不使用英语以外的任何语言,那么HashMap就太过分了:您可以简单地使用26个代表a..Z的计数数组。如果您需要支持Unicode,那么问题当然要复杂得多,因为您不仅需要处理数千种不同类型的字母,还必须定义“字母”(幸运的是,存在有助于此的字符属性数据)和“小写/大写”(请注意,有些语言没有大小写,有些语言可以将两个小写字母映射为一个大写字母,反之亦然……。更不用说规范化了:)

另一个选项是:

  • 从字符串(标点符号、空格)中去掉所有您不关心的字符
  • 把它改成小写
  • 对字符串排序
  • 与引用字符串比较(使用
    .equals
  • 不过我怀疑你的方法更快

    编辑:

    既然@nibot甚至不同意我的建议,而且我也不是一个在没有证据的情况下来回争论的人

    它们的实现非常相似:

  • 将行转换为小写
  • 忽略非字母字符
  • ?
  • 检查3的结果。匹配第一行的结果
  • 那个?第一部分是:

    • 制作字符计数的
      HashMap
    • 字符排序
    • 制作26 int数组(最终的哈希表解决方案,但仅适用于拉丁字母表)
    我用这个来运行它们:

    public static void time(String name, int repetitions, Function function,
            int expectedResult) throws Exception {
        long total = 0;
        for (int i = 0; i < repetitions; i++) {
            System.gc();
            long start = System.currentTimeMillis();
            int result = function.call();
            long end = System.currentTimeMillis();
            if (result != expectedResult) {
                System.out.println("Oops, " + name + " is broken");
                return;
            }
            total += end - start;
        }
        System.out.println("Executution of " + name + " took "
                + (total / repetitions) + " ms on average");
    }
    
    如果满足以下条件,
    HashMap
    one可以得到显著改进:

    • 有一种方法可以在
      HashMap
      中指定的默认值,还有一种方法可以获取和递增(因此只有一个查找,而不是2个)
    但是,这些不在标准库中,所以我忽略它们(就像大多数使用Java的程序员一样)

    这个故事的寓意是大O不是一切。你需要考虑的是开销和大小<强> n<强>。在这种情况下,n相当小,而
    HashMap
    的开销非常大。对于更长的线路,这种情况可能会改变,但不幸的是,我不想知道盈亏平衡点在哪里


    如果你仍然不相信我,考虑GCC在某些情况下使用它的C++标准库。< /P> < P>在卡尔KuncTelt的回答中建立(并解决你对支持多个字母表的关注):

    • 创建接口(比如)AnagramKey和AnagramKeyFactory。将应用程序的其余部分设计为不知道所使用的键的类型

    • 创建一个AnagramKey接口的实现,该接口在内部使用
      int[]
      表示字符计数

    • 创建AnagramKey接口的第二个实现,该接口使用
      HashMap
      表示字符计数

    • 创建相应的工厂接口

    • 使用命令行参数、区域设置或其他方式在表示键的两种方式之间进行选择

    注:

  • 在非字母语言的语境中,或在将多种语言混合成一个“句子”的话语中,“字谜”是否有意义尚不清楚。另外,我不知道法语中的字谜是否忽略了字符上的重音。无论如何,我很想把所有这些案例都判定为“超出范围”。。。除非你有明确的要求来支持他们

  • 在计数数组的字符范围内,
    int[]
    HashMap
    使用更少空间时,盈亏平衡密度在15个字符中逐渐接近1个字符。(具有这些键/值类型的HashMap中的每个条目占用15个32位字的区域。)这不考虑
    HashMap
    节点和hash数组节点的开销

  • 如果对字谜的长度进行限制,则可以使用
    short[]
    甚至
    byte[]
    进行字符计数,从而节省更多空间


  • 嗯,我想到了
    Execution of testWithHashMap took 158 ms on average
    Execution of testWithSorting took 76 ms on average
    Execution of testWithArray took 56 ms on average