Prefix search through list/dictionary using .NET StringDictionary?

前端 未结 5 1191
情歌与酒
情歌与酒 2021-01-15 09:06

I was wondering if .NET offers any standard functionality for doing a prefix search through a list or a dictionary object. I came across the StringDictionary, b

5条回答
  •  长发绾君心
    2021-01-15 09:39

    Below is a basic implementation of a set of strings that can be searched efficiently by prefix.

    The idea is to keep all the words of the set in a trie, and when queried to find all words that start with some prefix, we find the node corresponding to the last character in the prefix, and in DFS from there we collect and return all its descendants.

    public class PrefixSearchableSet
    {
        private readonly Dictionary _letterToNode = new Dictionary();
        private bool _isEmptyWordIncluded;
    
        public PrefixSearchableSet(IEnumerable 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 GetWords(string prefix)
        {
            List words = new List();
    
            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 letterToNode, char key)
        {
            if (letterToNode.TryGetValue(key, out TrieNode node)) return node;
            return letterToNode[key] = new TrieNode();
        }
    
        private class TrieNode
        {
            public Dictionary Children { get; } = new Dictionary();
    
            public string Word { get; set; }
    
            public void CollectWords(List words)
            {
                if (Word != null) words.Add(Word);
                foreach (TrieNode child in Children.Values)
                {
                    child.CollectWords(words);
                }
            }
        }
    }
    

提交回复
热议问题