【问题标题】:Fastest way to union two strings合并两个字符串的最快方法
【发布时间】:2015-05-28 19:15:55
【问题描述】:

我正在寻找一种快速的方法来组合两个字符串的字符(根据集合论)。例如 'copy' union 'creepy' 应该给出 'copyre'。我需要获取由 很多 短字符串(我认为最多 50 个字符)组成的文件中使用的所有字母。

此刻我:

  • 从文件中读取字符串
  • 遍历其字母并使用二分搜索在已使用字母集(存储为排序字符串)中搜索每个字母。

这需要处理 unicode 字符,因此使用布尔值制作表格并标记出现的每个字母是行不通的。任何想法如何使这更快?

【问题讨论】:

  • 'copyrep' 里面有两次 p,这是故意的吗?如果有,这个工会的规则是什么?
  • 不是故意的,我的错
  • 你想对奇怪的东西做些什么,比如组合变音符号、从左到右的标记、零宽度空间、整罐虫子?
  • 您是否多次处理每个单独的字符串?如果是这样,那么我建议对它们进行预排序/规范化,以便您可以进行简单的并行迭代以稍后形成单个联合。

标签: string performance


【解决方案1】:

如果您需要获取所有字母,我建议使用位向量,每个字母都有一个条目并标记外观。位向量是一个整数数组,它被解释为一个 n 元布尔数组,其中第 n 位表示第 n 个布尔值。访问时间是恒定的。如果字符集的大小太大或先验未知,则需要不同的集实现。但是,在任何情况下,您都应该为集合使用现有的数据结构(例如 this one),而不是自己发明。

算法如下所示:

for (int i = 0; i < len; i++) 
   bits[mem[i]] = true;

这是线性时间。我想,情况并没有好转。您可能可以通过使用巧妙的 CPU 对齐和并行化来获得一些常数因子 - 不过,这取决于问题的大小。

【讨论】:

  • 它需要使用 unicode 字符,所以列出所有可能性超出了我的范围。如果每次出现字母时我都添加一个新条目,这与我当前的解决方案有何不同?我仍然需要在向量中搜索字母。或者只是我看不出怎么更快?
  • @ElevenFortyOne 你可以使用哈希集
  • @ElevenFortyOne:您的目标是嵌入式设备吗?一个完整的 Unicode 位图“只”需要 136k 的 RAM,除非您的数据集特别疯狂,否则除了一个小的缓存友好子集之外,您几乎看不到任何东西。只需对字符串进行两次传递,即可仅手动清除最后出现的标志。
【解决方案2】:

获取第一个字符串,然后将每个字符放入一些散列集,例如java.util.HashSet

获取第二个字符串并将每个字符放在同一个哈希集中。

遍历集合以获取“联合字符串”。这个字符串可能是随机顺序的。

另一个有希望的解决方案是使用专门用于整数集的数据结构 - 因为字符在内部以某种方式表示为整数。

这种整数集的一个例子是 Zach Tellman 为 Clojure 编写的 data.int-map。它被描述为“Okasaki 和 Gill 的“快速合并整数映射”的直接端口,可以在 http://ittc.ku.edu/~andygill/papers/IntMap98.pdf 找到。” Okasaki 和 Gill 的工作似乎是在 Haskell 中实现的。

在其他语言中可能有类似的高效整数集实现。

【讨论】:

    【解决方案3】:

    当您谈论上限为 50 个 unicode 字符的输入大小时(我假设您的平均大小写要小得多),那么很多算法选项就会出现。我们通常关注裸机微优化。凭借这些极小的输入大小,冒泡排序实际上可以胜过快速排序。

    如果我们试图计算两个 8 个字符的字符串之间的并集,例如,构建辅助结构或执行动态排序的成本可能需要比简单的甚至具有二次复杂度的蛮力解决方案。我认为这可能是正确的,即使您可以设法仅出于内存/缓存相关的原因重用相同的数据结构。

    如果您可以提前对两个字符串进行预排序(例如:已经在文件中存储了排序的字符串),那么您可能会得到改进,因为这样可以在单个线性传递中找到两个排序字符串之间的联合(只需使用两个指针/索引)。这是假设您可以在这个时间关键的循环之外提前对所有这些进行排序。

    否则,您可能正在查看微优化并希望在此处获取分析器。考虑到相对的努力/奖励比率,其中最高优先级可能与内存相关。例如,您不希望不断分配和释放字符串对象(并且可能会导致更多缓存未命中,具体取决于您之后的操作)并希望重用相同的缓冲区,除非您的字符串只是在硬件上分配堆栈(在这种情况下分配/解除分配通常非常便宜)。

    接下来可能是多线程,但对于这条路线,您可能希望每个线程做更多的工作,而不是计算两个小字符串之间的联合。调度开销可能会超过这里的好处,因此您希望每个线程/并行迭代计算多个(可能很多,比如数百个)字符串的联合。

    【讨论】:

      【解决方案4】:

      获得并集的一种快速而肮脏的方法是连接两个字符串,对该数组进行排序,然后创建一个没有任何重复项的新数组。

      Java 中的一个(未经测试的)示例是

      String copy = "copy";
      String creepy = "creepy";
      char[] chars = (copy + creepy).toCharArray();
      
      java.util.Arrays.sort(chars); //puts duplicates side by side
      
      int currentChar = -1; //no risk of initial collision since chars >= 0
      int setSize = 0;      //set size, pointer when "compacting" the set
      
      //ignore duplicates, reuses the char[] - garbage in the end!
      
      for (int pos = 0; pos < chars.length; pos++) {
          if (currentChar != (int)chars[pos]) {
              chars[setSize] = chars[pos];
              setSize++;
              currentChar = chars[pos];
          }
      }
      
      // please note that Strings are both immutable
      // and can be held in memory for long times, don't allocate them for
      // intermediate results if you can avoid it.
      
      String result = new String(java.util.Arrays.copyOf(chars, setSize));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-08
        • 2021-07-06
        • 2016-05-02
        • 2012-03-15
        • 1970-01-01
        • 1970-01-01
        • 2011-01-19
        • 2012-02-04
        相关资源
        最近更新 更多