字符串数组只包含字谜?

时间:2011-10-03 23:47:21

标签: java algorithm hashmap anagram

我接受过关于字谜的练习,看起来很容易让我怀疑我错过了什么。 我实施的解决方案是我即将提出的解决方案,我想问你是否可以考虑使用我的解决方案进行任何优化,改变方法或问题。 我用Java实现了算法。

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

出租车契约最疯狂的Min鱼L .. 驾驶室契约最疯狂的Min鱼L 驾驶室契约洗牌百万不会 驾驶室契约洗牌百万镇

程序应该返回True。 输入:

出租车契约最疯狂的Min鱼L .. 驾驶室契约最凶小的鲤鱼Lolls喜 驾驶室契约洗牌百万不会 驾驶室契约洗牌百万镇

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

现在,我认为非常简单:

  • 我创建了2个HashMap:ref和cur。
  • 我解析文本的第一行,填写参考号。我只计算字母。
  • 对于每一行,我将该行解析为cur并检查cur.equals(ref):if so return false
  • 如果我到达文本的末尾,则意味着每一行都是每行的一个字谜,所以我返回true。

而且......就是这样。 我尝试使用88000行的输入文本,它的工作速度非常快。

有何评论?建议?优化

非常感谢您的帮助。

3 个答案:

答案 0 :(得分:5)

另一种选择是:

  1. 从字符串(标点符号,空格)中删除您不关心的所有字符
  2. 将其设为小写
  3. 对字符串进行排序
  4. 与参考字符串(.equals
  5. 比较

    我怀疑你的方式更快。<​​/ p>

    编辑:

    因为@nibot不同意我甚至暗示这一点,并且我不是一个没有证据来回争辩的人,here's three solutions

    它们的实施非常相似:

    1. 将行转换为小写
    2. 忽略非字母字符
    3. 检查结果3.匹配第一行的结果
    4. ?部分是以下之一:

      • 制作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");
      }
      

      我的文件类似于OP发布的文件,但是显着更长,从最后使用非anagram大约20行,以确保算法都能正常工作。

      我一直得到这样的结果:

      Execution of testWithHashMap took 158 ms on average
      Execution of testWithSorting took 76 ms on average
      Execution of testWithArray took 56 ms on average
      

      如果符合以下条件,HashMap可能会得到显着改善:

      但是,这些不在标准库中,所以我忽略了它们(就像大多数使用Java的程序员一样)。

      故事的寓意是大O不是一切。您需要考虑 n 的开销和大小。在这种情况下, n 相当小,HashMap的开销很大。如果线条较长,可能会发生变化,但不幸的是,我不想找出收支平衡点的位置。

      如果您仍然不相信我,请考虑GCC在其C ++标准库中使用 insertion sort

答案 1 :(得分:3)

假设您的HashMap是来自(字符)的映射 - &gt; (字符串中出现的次数),你几乎拥有它。

我认为你应该忽略空格和标点符号,并将大写和小写字母视为相同。如果你没有使用除英语以外的任何语言,那么HashMap就是过度杀戮:你可以简单地使用一个包含A..Z的26个数组。如果你需要支持Unicode那么问题当然要复杂得多,因为你不仅需要处理数千种不同类型的字母,而且你还必须定义'字母'(幸运的是存在字符属性数据,帮助这个)和'小写/大写'(请注意,有些语言没有大小写,有些语言可以将两个小写字母映射为单个大写字母,反之亦然......)。更不用说正常化了:))

答案 2 :(得分:2)

建立@Karl Knechtel的答案(并解决您对支持多个字母表的担忧):

  • 创建接口(比方说)AnagramKey和AnagramKeyFactory。将应用程序的其余部分设计为与所使用的密钥类型无关。

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

  • 创建AnagramKey接口的第二个实现,它使用HashMap<Character, Integer>来表示字符数。

  • 创建相应的工厂接口。

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

注意:

  1. 目前尚不清楚“字谜”在非字母语言的背景下是有意义的,还是将多种语言混合成“句子”的话语。另外,我不知道(例如)法语中的字谜是否忽略了字符的重音。无论如何,我很想把所有这些案件都排除在“超出范围”之外......除非你明确要求支持它们。

  2. int[]使用的空间小于HashMap<Character, Integer>的均衡密度在计数数组中的字符范围内渐近约为15个字符。 (具有这些键/值类型的HashMap中的每个条目占用15个32位字的区域。)并且没有考虑HashMap节点和散列数组节点的开销......

  3. 如果您对字谜的长度设置限制,则可以使用short[]甚至byte[]来保存更多空间。