【问题标题】:Searching for the beginning of a string in an array in C#在 C# 中的数组中搜索字符串的开头
【发布时间】:2010-10-07 03:47:18
【问题描述】:

我有一个包含很多路径的列表。我有一个特定的路径,我想对照这个列表检查是否有任何路径使用这个路径,即:

f.StartsWith(r.FILENAME) && f != r.FILENAME

最快的方法是什么?

编辑:来自以下答案的完整功能:

static bool ContainsFragment(string[] paths, string fragment)
{
    // paths **must** be pre-sorted via Array.Sort(paths);
    if (paths.Length == 0) return false;
    int index = Array.BinarySearch(paths, fragment);
    if (index >= 0 && index+1 < paths.Length)
    { //we found it 
        if (paths[index + 1].StartsWith(fragment) &&
            paths[index + 1].EndsWith(".manifest"))
        {
            return true;
        }
    }
    return false;
}

【问题讨论】:

  • (注意我更新了你的 cmets)

标签: c# .net arrays string


【解决方案1】:

最快的方式可能是二分查找:

static bool ContainsFragment(string[] paths, string fragment)
{
    // paths **must** be pre-sorted via Array.Sort(paths);
    if (paths.Length == 0) return false;
    int index = Array.BinarySearch(paths, fragment);
    // we want the index of the *next highest* path
    if (index < 0) { // no match
        index = ~index; 
    } else { // exact match
        index++; // for strict substring (non-equal)
    }
    return index < paths.Length && paths[index].StartsWith(fragment);
}

但是,如果您只进行几次排序,则对数组进行排序的成本将超过任何好处;在这种情况下,只需扫描数组 - 使用 LINQ 等,或者只是:

bool found = false;
for(int i = 0 ; i < paths.Length ; i++) {
    if(paths[i].StartsWith(fragment) &&
          paths[i].Length != fragment.Length)
    {
        found = true;
        break;
    }
}

【讨论】:

  • 检查将从 50000 到几十万次,所以排序是有回报的。我想要的是,如果有任何匹配不是完全匹配(总是会有一个完全匹配),而是一个开始相同但不完全相同的匹配。
  • 我仍然觉得这不是最快的方法,因为我相信我们仍在循环遍历数组直到找到我们想要的匹配。
  • 不,二分搜索不会循环 - 它减半。因此它将分 15 步搜索 50000 个。我怀疑(因为总会有完全匹配的)你还需要在匹配后检查 next 项...
  • 如果您在我的问题中看到,我已经更新了一个我相信会起作用的功能。我不太明白你用 index = ~index; 做什么以及您拥有的最后一个 return 语句
  • 其实和你的代码很相似;如果 没有找到,~index 返回下一项的索引 - 与代码中的“index + 1”相同。在我的“找到匹配”版本中,我只是使用了 index++,但结果是一样的。
【解决方案2】:
var matches = list.Where(f => f.StartsWith(r.FILENAME) && f != r.FILENAME);

或者如果你只关心存在:

bool any = list.Any(f => f.StartsWith(r.FILENAME) && f != r.FILENAME);

这是假设您使用的是 .NET 3.5,不可否认 - 否则 List&lt;T&gt; 中有类似的方法,您可以使用匿名方法。

【讨论】:

  • 这并不比做一个简单的 for 循环快,甚至可能更慢。
【解决方案3】:

我觉得这个相对简单的问题有趣的是“有效”答案的数量,这取决于您如何定义“最快”。

  • 如果最快意味着“成本最低”,那么 Marc 的二分搜索方法听起来像是您的答案。
  • 如果最快意味着“最快实施”,那么 Jon 的 list.Any 方法调用是合适的。
  • 如果最快意味着“蛮力”,那么您可能需要考虑并行化搜索。就所需的处理而言,它的成本会更高,但可能会根据您的服务器资源更快地执行。 PLINQ 为您提供了一个很好的起点。

【讨论】:

  • 你说的很对。我的问题可能是“成本最低”和“蛮力”的结合。在这里的一个示例中,我需要比较 250 000 条记录(即集合中的 foreach 记录,如果有另一个具有相同开头但在记录集中更长的记录。)(在下一条评论中继续)
  • 我需要按顺序输出记录,但检查的顺序不相关。因此,Marc 的二进制搜索和多线程方法的组合可能是最好的。 PLINQ 可能会为我自动执行此操作,但我有疑问 :) 不过感谢您的提示。
  • PLINQ 只会并行化迭代,这可以加速某些搜索算法。如果列表已经排序,二分查找是最好的选择,不能并行化。
  • 排序,但是,可以。您可能想提出另一个问题,询问 250k 记录列表的最佳排序算法。与搜索相比,多处理器/内核的可用性对这一点的影响要大得多。
猜你喜欢
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 2022-11-01
  • 2011-02-17
  • 2012-02-10
相关资源
最近更新 更多