【问题标题】:Delphi removing large amount of partial matching strings from large arrayDelphi 从大数组中删除大量部分匹配的字符串
【发布时间】:2017-02-19 03:12:03
【问题描述】:

我有 2 个字符串数组,数组 #1 包含大约 250 万个字符串,数组 #2 包含大约 450 万个字符串。我需要检查数组 #2 中的字符串是否在数组 #1 的字符串中,然后删除它们。

由于“字符串包含另一个字符串”的要求,我不能使用任何二进制搜索等,并且该过程目前需要 30 多个小时。

“字符串包含另一个字符串”的意思是,例如,数组 #1 包含字符串“船屋”,数组 #2 包含某个地方“房子”,所以“房子”在“船屋”中,这意味着我将拥有从数组 #1 中删除“船屋”。

示例(不是实际的,也不起作用)代码来更好地解释它:

for i:=0 to length(array1)-1 do
begin
  for j:=0 to length(array2)-1 do
  begin
    if ansicontainstext(array1[i],array2[j]) then
    begin
      martrecordtoremove;
      break;
    end;
  end;
end;

所有字符串大约需要 30 小时。

所以我的问题是,有什么方法可以更快地做到这一点?

【问题讨论】:

  • 那么你的问题是什么?

标签: arrays string delphi search


【解决方案1】:

为了避免简单的字符串搜索,您必须利用字符串搜索算法来快速搜索文本(wiki) 中的整个模式集。

最简单的实现是 Rabin-Karp 算法。
最坏情况下的最佳复杂性是 Aho-Corasick 之一。

两种算法的平均情况都很接近,因此值得首先检查 R-K 速度是否适合您的目的。


另一个可能的问题 - martrecordtoremove 是如何实现的?为了有效地删除,您应该消除多次内存重新分配。

【讨论】:

    【解决方案2】:

    你可以进行二分搜索,但索引会很大(大约 5000 万个条目,不是灾难)。最简单的尝试创建一个 sphinxsearch 索引,在里面你有一个参数来设置 word 里面的词(在内部这意味着船屋 sphinx 搜索会将这些关键字添加到它的索引中):

    houseboat
           at
          oat
         boat
    etc..
    

    搜索会立即返回,索引的创建应该很快

    【讨论】:

      【解决方案3】:

      在我看来,部分问题在于您循环了 2.5M * 4.5M 次。您是否尝试过使用 TStringList 而不是数组?如果您的数组是 TStringList(例如 SA1、SA2),您可以编写如下代码:

      var
        i, j: integer;
      begin
        SA1.CaseSensitive:=false;
        SA2.CaseSensitive:=false;
        SA1.Sort;
        SA2.Sort;
        for i := 0 to SA2.Count-1 do
        begin
          while true do
          begin
            //if we delete all the SA1 items, no more processing is required
            if SA1.Count=0 then
              exit;
            //find the occurrence of SA2[i] in SA1
            SA1.Find(SA2[i], j);
            //Check if the line at item j in SA1 contains the text of SA2[i]
            if Pos (SA2[i], SA1[j]) > 0 then //yes, then we delete it
              SA1.Delete(j)
            else
              if (j-1>=0) and (Pos (SA2[i], SA1[j-1]) > 0) then //else check the previous line to see if that has the text
                SA1.Delete(j-1)
              else
                if (j+1<SA1.Count) and (Pos (SA2[i], SA1[j+1]) > 0) then //else check the next line
                  SA1.Delete(j+1)
                else //otherwise break out of while loop
                  break;
          end;
        end;
      end;
      

      我们在不区分大小写(且已排序)的字符串列表上使用 Find。这仅在 4.5M 项目列表中运行一次 - 并从 2.5M 数组中删除项目(即 SA1 在循环期间缩小)。在循环结束时,SA1 将只包含您想要的字符串(在 SA2 中不存在)。也许您应该尝试一下,看看它是否适合您(并希望提高性能)?

      希望这会有所帮助。

      更新 20170221: 我更新了代码以在删除之前使用 Find 来查找字符串索引(SA1 中的 SA2)。而且我还更新了代码以允许 SA2 字符串在 SA1 中多次出现的可能性。我还更新了代码,使 Stringlist 操作不区分大小写。

      【讨论】:

      • 我认为它不会找到部分匹配的条目。 Pos() 会找到它,但不会找到 indexof 行,因为它只是部分匹配,例如 sa2 包含“house”,而 sa1 包含“houseboat”,因此 indexof 不会找到它。
      • 你是对的。我将更新代码以反映这一点。此外,我意识到 SA1 可能包含多次出现的 SA2 文本(船屋、家庭等)。所以我的新代码也将解决这个问题。 (如果你实现了这个,知道新进程运行多长时间会很有趣——希望不到 30 小时!)。
      • 谢谢,但它比使用 madStrings 组件中的 postext 的简单循环慢..
      猜你喜欢
      • 1970-01-01
      • 2022-11-14
      • 1970-01-01
      • 2016-07-23
      • 2013-05-17
      • 2015-02-07
      • 2017-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多