【问题标题】:How do I (quickly) find the longest matching string in C#/.Net如何(快速)在 C#/.Net 中找到最长的匹配字符串
【发布时间】:2011-12-01 01:05:07
【问题描述】:

我需要对一组项目进行一些查找操作。

首先我需要看看是否有直接匹配。这很简单,因为我在 Dictionary<String,MyObjectType> 中有条目,所以我可以转到 dictionary["valuetofind"]

如果没有直接匹配,那么我需要做一个开始匹配,但它必须是返回的最长匹配:

记录示例:

String   Record
0        A
01       B
012      D
02       B
03       C

查询示例:

Query         Result 
0             A    - Because 0   is the longest match
01            B    - Because 01  is the longest match
023456        B    - Because 02  is the longest match
012           D    - Because 012 is the longest match
0123456       D    - Because 012 is the longest match
03456         C    - Because 03  is the longest match
04            A    - Because 0   is the longest match
0456          A    - Because 0   is the longest match
1             Null - No Match

框架中是否有在后台实现具有哈希或树结构的类来执行此类操作,还是我需要自己编写一些东西?

编辑 到目前为止,我所拥有的是按模式字符串长度排序的列表,然后我一一检查条目以查看查询是否以记录开头。这适用于大多数情况,因为我们(还)没有大列表,但对于没有匹配的情况确实有昂贵的成本。

我缺乏让谷歌给我与哈希集、列表和字典无关的页面的词汇。我发现的所有研究都指向基于树的结构,但没有人指出 .NET Framework 中是否已经实现。

【问题讨论】:

  • 下面的字典方法很可能是O(n^2 logn)。 trie 可能会起作用,而且只会是O(n logn)
  • 在您有一个非常大的搜索集的情况下,类似 Trie 的结构将是解决此问题的最快方法。 en.wikipedia.org/wiki/Trie
  • @leppie:在您的订单近似值中,对数项来自哪里?一个构建良好的 trie 可以在 O(m) 时间内搜索到长度为 m 的字符串; trie 中的节点数不是一个因素。
  • @EricLippert:你是对的。不知道我在想什么;p 我知道第一个只是一个大拇指的猜测(错误地基于 'contains' 而不是 'startswith')。

标签: c# collections


【解决方案1】:

Leppie 和 Spender 是正确的;如果数据集变大,您想要有效解决此问题的数据结构是“trie”,或者,如果您真的很迷,则可以使用 DAWG——有向无环字图。如果字符串有许多共同的后缀,DAWG 的内存性能会更好,但它们更昂贵且难以构建和更新,所以从尝试开始。

您的简单案例将尝试如下所示:

           ROOT
            |
           0|
            |
            A
          / | \
         /  |  \
       1/  2|  3\
       /    |    \
      /     |     \
     B      B      C
     |
    2|
     |
     D

所以要查找 023456,从根开始,沿着标记为 0 的分支向下找到 A,然后沿着分支 2 向下找到 B,此时没有分支 3,所以你完成了。

p>

顺便说一句,这也是在给定字典和一组字母的情况下查找最长的 Scrabble 单词的数据结构;本质上是同一个问题。

.NET 框架中没有内置 trie 数据结构,但构建起来并不难。我有一个不可变的 trie,就在我一直想写博客的某个地方;如果有的话,我会在这里发布一个链接。

【讨论】:

  • 我们出于多种目的广泛使用尝试(和类似尝试的图),但我最喜欢的是我们网站在大量项目上的超快速(且非 CPU 密集型)自动完成.就内存使用而言,它的成本很高,但它使搜索变得即时。在我看来,这是一个被严重低估的数据结构。很高兴看到你在你的博客上发布一些关于它的东西。
  • 我实际上已经在 J​​avaScript 中实现了一次 trie,与 spender 完全相同;服务器将数据作为看起来像 {'e': {'x': {'a': {'m': {'p': {'l': {'e': {'value': 'Example'}}}}}}}} 的项目数组返回,我们使用 jQuery.extend 通过一个方法调用从中构建一个 trie。
【解决方案2】:

一个相当简单的方法是brute force他们。我假设你有一个 Dictionary<string, string> _lookupTable 来保存你的查找

string Find(string query)
{
    var retval = null;
    while(!string.IsNullOrEmpty(query) && retval == null)
    {
        if(!_lookupTable.TryGetValue(query, out retval))
            query = query.Substring(0, query.Length-1);
    }
    return retval;
}

【讨论】:

  • 这对我有用。我对这个实现进行了一些测试,它的速度足以满足我们的需求。
【解决方案3】:

您可以扫描整个字典以查找最长的匹配项。

        string sQuery = "01234";

        int iMaxLength = 0;
        foreach (KeyValuePair<String, String> kVP in mD)
        {
            if (sQuery.Contains(kVP.Value) && (kVP.Value.Length > iMaxLength))
            {
                iMaxLength = kVP.Value.Length
                result = (whatever...)
            }
        }

【讨论】:

    【解决方案4】:

    从外观上看,您应该使用简单地按长度排序的二叉树,然后查找第一个匹配项。我不认为像二叉树这样的东西已经在 c# 中实现了,但是快速搜索一下就会发现很多网站有人这样做了。

    【讨论】:

      猜你喜欢
      • 2014-09-21
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2014-06-26
      • 1970-01-01
      • 2021-10-28
      • 1970-01-01
      • 2020-08-11
      相关资源
      最近更新 更多