【问题标题】:Fastest method of finding words in a collection with wildcard letters使用通配符在集合中查找单词的最快方法
【发布时间】:2021-05-06 01:27:07
【问题描述】:

我有大约 200,000 个单词需要找到与可以包含任意数量的字母通配符的单词匹配的单词。我还需要查找不带任何通配符的单词的选项。

我已按长度将单词分成集合:

static readonly HashSet<string>[] _validWords = { 
    new HashSet<string>(StringComparer.OrdinalIgnoreCase), // 3 letter words
    new HashSet<string>(StringComparer.OrdinalIgnoreCase), // 4 letter words
    new HashSet<string>(StringComparer.OrdinalIgnoreCase), // 5 letter words
    new HashSet<string>(StringComparer.OrdinalIgnoreCase), // 6 letter words
    new HashSet<string>(StringComparer.OrdinalIgnoreCase), // 7 letter words
    new HashSet<string>(StringComparer.OrdinalIgnoreCase), // 8 letter words
    new HashSet<string>(StringComparer.OrdinalIgnoreCase)  // 9 letter words
};

搜索特定单词很简单:

public static bool IsValid(string word) {
    return word.Length >= GameplaySettings.Instance.MinWordLength && _validWords[word.Length - 3].Contains(word);
}

这是我目前使用正则表达式查找通配符的实现(最终我想得到 all 匹配的单词,但现在只找到一个(或没有)就可以了。):

public static bool IsValidRegex(string pattern, int length) {
    pattern = $"^{pattern}$";

    foreach (string word in _validWords[length - 3]) {
        if (Regex.Matches(word, pattern, RegexOptions.Singleline).Count > 0) { return true; }
    }

    return false;
}

可以有任意数量的通配符(例如,all 字母甚至可以是通配符),而且它目前的性能不如我希望的那样。

所以我想知道是否有更有效的方法!

感谢您的任何帮助/建议!

【问题讨论】:

  • 在这里使用 hashet 没有任何好处。也许您需要想出某种可以搜索的树结构。
  • “不幸的是,我尝试的实现没有奏效” - 因为HashSet&lt;T&gt; 在这种情况下根本没有帮助,如果你提供一个适当的问题,你的问题会更好minimal reproducible example 展示了您尝试过的内容,并详细说明了实际做了什么、您希望它做什么以及具体您需要什么帮助。
  • @PeterDuniho - 我是否错误地认为用于查找特定条目的 HashSet 比迭代数组或列表更快?它是一个 HashSet,因为这是必需的功能,而不是复制数据,我正在寻找使用相同容器的解决方案。
  • HashSet 的查找速度更快,因为它不会遍历其所有项目:它使用您正在查找的对象的哈希码在哈希集中内部找到正确的分区,然后进行比较每个项目都有一个匹配的哈希码,直到找到有问题的项目。因此,您可能只比较 1(取决于哈希码冲突),而不是比较 .Count 项目。迭代哈希集并不比迭代数组或列表快,而这正是您正在做的。
  • @Llama 是的,这是我的理解,所以我使用 HashSet 是因为我确实需要查找特定的单词,而且我还需要单独查找通配符匹配项。我已经用我当前的实现更新了这个问题,希望能更好地解释它。

标签: c# hashset


【解决方案1】:

由于您的通配符只能匹配一个字母,因此问题并不难。如果你需要支持可变长度子字符串,我建议你去阅读一些关于正则表达式如何工作的科学文献。

这是一个相当基本的第二年 comp-sci“数据结构和算法”练习。在每个Node 中使用Dictionary 可能不会是最快/最节省内存的。但我会这样解决问题;

class Node
{
    public bool endWord;
    public Dictionary<char, Node> next;
}

public class Words
{
    private Node root = new Node { endWord = false };
    public const char wildcard = '_';

    public void DefineWord(string word)
    {
        var node = root;
        foreach (var c in word)
        {
            if (node.next == null)
                node.next = new Dictionary<char, Node>();
            if (node.next.TryGetValue(c, out var nextNode))
            {
                node = nextNode;
            }
            else
            {
                node = node.next[c] = new Node { endWord = false };
            }
        }
        node.endWord = true;
    }

    private bool IsValid(ReadOnlySpan<char> word, Node node)
    {
        if (word.IsEmpty && node.endWord)
            return true;
        if (node.next == null)
            return false;

        if (word[0] == wildcard)
        {
            word = word.Slice(1);
            foreach(var n in node.next.Values)
            {
                if (IsValid(word, n))
                    return true;
            }
        } else if (node.next.TryGetValue(word[0], out var nextNode))
            return IsValid(word.Slice(1), nextNode);
        return false;
    }

    public bool IsValid(string word)
        => IsValid(word, root);

    public static void Test1()
    {
        var words = new Words();
        words.DefineWord("APE");
        words.DefineWord("APPLE");
        words.DefineWord("BEAR");
        words.DefineWord("BEER");
        words.DefineWord("PEAR");
        words.DefineWord("PEER");
        words.DefineWord("PEERS");

        Assert.True(words.IsValid("APE"));
        Assert.True(words.IsValid("APPLE"));
        Assert.True(words.IsValid("PEAR"));
        Assert.True(words.IsValid("PEER"));
        Assert.True(words.IsValid("PEERS"));
        Assert.True(!words.IsValid("PLIERS"));
        Assert.True(words.IsValid("PE_R"));
        Assert.True(words.IsValid("_EAR"));
        Assert.True(words.IsValid("_E_R"));
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 2018-01-08
    • 2010-09-10
    相关资源
    最近更新 更多