使用.NET StringDictionary通过列表/字典进行前缀搜索?

时间:2009-12-14 16:58:02

标签: c# string search prefix

我想知道.NET是否提供了通过列表或字典对象进行前缀搜索的任何标准功能。我偶然发现了StringDictionary,但无法弄清楚它是否能为我做到这一点。

如果它可以进行前缀搜索,它是否也能进行子串搜索或让我使用正则表达式进行搜索?

提前致谢。

5 个答案:

答案 0 :(得分:5)

StringDictionary只是一个哈希表,其中键和值为string s。这在泛型之前存在(当Dictionary<string, string>不可能时)。

此处所需的数据结构为trieCodeProject上有实现:

  1. Phone Directory Implementation Using TRIE
  2. A Reusable Prefix Tree using Generics in C# 2.0
  3. 或者,如果你是那种人,请自己动手(见CLRS)。

答案 1 :(得分:4)

我不相信StringDictionary支持前缀搜索,但是如果你使用SortedList<,>,你可以在键的范围内进行二进制搜索,直到找到前缀之前和之后的第一个条目。

答案 2 :(得分:2)

我认为StringDictionary是旧学校(pre-generics)。您可能应该使用Dictionary(Of String, String),因为它实现了IEnumerable(想想LINQ)。一个非常lame thing about StringDictionary是它不区分大小写。

答案 3 :(得分:0)

我对此here进行了通用实现。

由于string实现了IEnumerable<char>,因此您可以将char用作TKeyElement的参数。

答案 4 :(得分:0)

以下是一组字符串的基本实现,可以通过前缀有效地搜索这些字符串。

这个想法是将集合中的所有单词保留在一个特里,当查询找到所有以某个前缀开头的单词时,我们找到与该前缀中最后一个字符相对应的节点,然后从DFS中找到该节点收集并返回其所有后代。

public class PrefixSearchableSet
{
    private readonly Dictionary<char, TrieNode> _letterToNode = new Dictionary<char, TrieNode>();
    private bool _isEmptyWordIncluded;

    public PrefixSearchableSet(IEnumerable<string> words = null)
    {
        if (words is null) return;
        foreach (string word in words)
        {
            AddWord(word);
        }
    }

    public void AddWord(string word)
    {
        if (word is null) return;

        if (word is "") _isEmptyWordIncluded = true;

        else
        {
            TrieNode node = FindOrAdd(_letterToNode, word[0]);
            foreach (char c in word.Skip(1))
            {
                node = FindOrAdd(node.Children, c);
            }

            node.Word = word;
        }
    }

    public List<string> GetWords(string prefix)
    {
        List<string> words = new List<string>();

        if (prefix is null) return words;

        if (prefix is "")
        {
            if (_isEmptyWordIncluded) words.Add("");
            foreach (TrieNode trieNode in _letterToNode.Values)
            {
                trieNode.CollectWords(words);
            }
            return words;
        }

        _letterToNode.TryGetValue(prefix[0], out TrieNode node);
        foreach (char c in prefix.Skip(1))
        {
            if (node is null) break;
            node.Children.TryGetValue(c, out node);
        }
        node?.CollectWords(words);

        return words;
    }

    private static TrieNode FindOrAdd(Dictionary<char, TrieNode> letterToNode, char key)
    {
        if (letterToNode.TryGetValue(key, out TrieNode node)) return node;
        return letterToNode[key] = new TrieNode();
    }

    private class TrieNode
    {
        public Dictionary<char, TrieNode> Children { get; } = new Dictionary<char, TrieNode>();

        public string Word { get; set; }

        public void CollectWords(List<string> words)
        {
            if (Word != null) words.Add(Word);
            foreach (TrieNode child in Children.Values)
            {
                child.CollectWords(words);
            }
        }
    }
}