加快字符串搜索算法

时间:2012-02-10 21:23:38

标签: c# string performance dictionary

我正在使用这个简单的算法搜索文档中的某些文本,并在我找到的页面上进行分页

for (int i = 1; i <= a.PageCount; i++)
{
    Buf.Append(a.Pages[i].Text);
    String contain = Buf.ToString();
    if (contain != "")
    {
        // Inside is dictionary of keys and value contain page where I found it
        foreach (KeyValuePair<string, List<string>> pair in inside)
        {
              if (contain.Contains(pair.Key))
                  inside[pair.Key].Add((i).ToString());
        }
    }

    Buf.Clear();
 }

我对此没有任何问题,但是当我搜索700页文档并且我正在寻找超过500个密钥时,它非常慢,大约需要1-2分钟才能通过,有什么办法可以加快速度吗?我正在使用c#

谢谢!

4 个答案:

答案 0 :(得分:4)

几点:

  • 摆脱Buf;只需将a.Pages[i].Text直接分配给contain
  • inside[pair.Key]浪费时间查找与该键相关联的值;时间浪费是因为你在pair.Value中对该对象的引用要便宜得多。
  • 如果您有一个整数值列表,为什么要将它们存储为字符串?

示例代码:

for (int i = 1; i <= a.PageCount; i++)
{
    String contain = a.Pages[i].Text
    if (contain != "")
    {
        // Inside is dictionary of keys and value contain page where I found it
        foreach (KeyValuePair<string, List<int>> pair in inside)
        {
            if (contain.Contains(pair.Key))
                pair.Value.Add(i);
        }
    }
}

最后,确保Pages确实使用基于一的索引。集合通常是零索引的。

编辑,因为Pages是字典:

foreach (KeyValuePair<int, Page> kvp in a.Pages)
{
    string contain = kvp.Value.Text;
    if (contain == "")
        continue;
    foreach (KeyValuePair<string, List<int>> pair in inside)
        if (contain.Contains(pair.Key))
            pair.Value.Add(kvp.Key);
}

您为第一个代码示例计算了多少次?时间可能因许多外部因素而异;一个方法的单次运行比另一个方法的单次运行更快或更慢的事实并没有真正告诉你多少,特别是因为我提出的建议可能无法解决大部分问题。

正如其他人所指出的那样,主要问题是你要拨打contain.Contains(pair.Key) 350,000次;这可能是你的瓶颈。您可以分析方法以确定是否为真。如果 为真,那么像Miserable Variable建议的Rabin Karp算法可能是你最好的选择。

答案 1 :(得分:3)

[[

编辑:以下是无关紧要的,因为你在循环结束时清除了Buf(虽然注意你真的不需要buf,string pageText = a.Pages[i].Text就是你所需要的)

什么是Buf

Buf.Append(a.Pages[i].Text);

这不会强迫Contains查看越来越大的字符串吗?我很惊讶你的700页没有内存不足。

]]

有更有效的方法可以查看any of a set of strings是否出现在另一个string中。例如,您可以准备键的树结构,这样您就不必多次比较。

请参阅Rabin-Karp Algorithm

考虑现有的第三方库,必须有一些。

答案 2 :(得分:0)

我没有700页要测试,但您可以尝试使用正则表达式:

var s = Stopwatch.StartNew();
var r = new Regex(string.Join("|", from x in inside select Regex.Escape(x.Key)));

for (int i = 1; i <= a.PageCount; i++)
{
    foreach (Match match in r.Matches(a.Pages[i].Text))
    {
        inside[match.Value].Add(i.ToString());
    }
}

Console.WriteLine(s.Elapsed);

答案 3 :(得分:0)

标准性能/调试程序 - 注释掉您的代码和度量。一次添加一个,直到它变坏。&#39;这可能是你的问题所在。

例如,你可以从评论整个foreach开始。

看起来有一些可能复杂/昂贵的物品在使用中 - 内部,Buf等。注释掉这些物品的使用并一次放回一个。