簡體   English   中英

最快的搜索算法開始

[英]fastest starts with search algorithm

我需要實現一個搜索算法,它只搜索字符串的開頭而不是字符串中的任何位置。

我是算法的新手,但從我所看到的情況來看,似乎他們通過字符串並發現任何事件。

我有一個字符串集合(超過100萬),每次用戶輸入擊鍵時都需要搜索。

編輯:

這將是一次增量搜索。 我目前使用以下代碼實現它,我的搜索范圍從超過100萬個可能的字符串返回300-700ms。 收集不是有序的,但沒有理由不能。

private ICollection<string> SearchCities(string searchString) {
        return _cityDataSource.AsParallel().Where(x => x.ToLower().StartsWith(searchString)).ToArray();
    }

我已經從Visual Studio Magazine中修改了本文中的代碼, 代碼實現了Trie

以下程序演示了如何使用Trie進行快速前綴搜索。

要運行此程序,您需要一個名為“words.txt”的文本文件,其中包含大量單詞。 你可以在這里從Github下載一個

編譯程序后,將“words.txt”文件復制到與可執行文件相同的文件夾中。

運行程序時,鍵入前綴(例如prefix ;))並按return鍵,它將列出以該前綴開頭的所有單詞。

這應該是一個非常快速的查找 - 有關更多詳細信息,請參閱Visual Studio Magazine文章!

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            var trie = new Trie();
            trie.InsertRange(File.ReadLines("words.txt"));

            Console.WriteLine("Type a prefix and press return.");

            while (true)
            {
                string prefix = Console.ReadLine();

                if (string.IsNullOrEmpty(prefix))
                    continue;

                var node = trie.Prefix(prefix);

                if (node.Depth == prefix.Length)
                {
                    foreach (var suffix in suffixes(node))
                        Console.WriteLine(prefix + suffix);
                }
                else
                {
                    Console.WriteLine("Prefix not found.");
                }

                Console.WriteLine();
            }
        }

        static IEnumerable<string> suffixes(Node parent)
        {
            var sb = new StringBuilder();
            return suffixes(parent, sb).Select(suffix => suffix.TrimEnd('$'));
        }

        static IEnumerable<string> suffixes(Node parent, StringBuilder current)
        {
            if (parent.IsLeaf())
            {
                yield return current.ToString();
            }
            else
            {
                foreach (var child in parent.Children)
                {
                    current.Append(child.Value);

                    foreach (var value in suffixes(child, current))
                        yield return value;

                    --current.Length;
                }
            }
        }
    }

    public class Node
    {
        public char Value { get; set; }
        public List<Node> Children { get; set; }
        public Node Parent { get; set; }
        public int Depth { get; set; }

        public Node(char value, int depth, Node parent)
        {
            Value = value;
            Children = new List<Node>();
            Depth = depth;
            Parent = parent;
        }

        public bool IsLeaf()
        {
            return Children.Count == 0;
        }

        public Node FindChildNode(char c)
        {
            return Children.FirstOrDefault(child => child.Value == c);
        }

        public void DeleteChildNode(char c)
        {
            for (var i = 0; i < Children.Count; i++)
                if (Children[i].Value == c)
                    Children.RemoveAt(i);
        }
    }

    public class Trie
    {
        readonly Node _root;

        public Trie()
        {
            _root = new Node('^', 0, null);
        }

        public Node Prefix(string s)
        {
            var currentNode = _root;
            var result = currentNode;

            foreach (var c in s)
            {
                currentNode = currentNode.FindChildNode(c);

                if (currentNode == null)
                    break;

                result = currentNode;
            }

            return result;
        }

        public bool Search(string s)
        {
            var prefix = Prefix(s);
            return prefix.Depth == s.Length && prefix.FindChildNode('$') != null;
        }

        public void InsertRange(IEnumerable<string> items)
        {
            foreach (string item in items)
                Insert(item);
        }

        public void Insert(string s)
        {
            var commonPrefix = Prefix(s);
            var current = commonPrefix;

            for (var i = current.Depth; i < s.Length; i++)
            {
                var newNode = new Node(s[i], current.Depth + 1, current);
                current.Children.Add(newNode);
                current = newNode;
            }

            current.Children.Add(new Node('$', current.Depth + 1, current));
        }

        public void Delete(string s)
        {
            if (!Search(s))
                return;

            var node = Prefix(s).FindChildNode('$');

            while (node.IsLeaf())
            {
                var parent = node.Parent;
                parent.DeleteChildNode(node.Value);
                node = parent;
            }
        }
    }
}

幾點想法:

首先,你的百萬字符串需要被排序,這樣你就可以“尋找”第一個匹配的字符串並返回字符串,直到你不再有匹配...按順序(通過C# List<string>.BinarySearch ,也許) 。 這就是你觸摸盡可能少的字符串的方式。

其次,你應該不要嘗試擊中字符串列表,直到輸入暫停至少500毫秒(給或拿)。

第三,你對浩瀚的問題應該是異步和可取消的,因為一定的努力將會被下一次擊鍵取代。

最后,任何后續查詢都應​​首先檢查新搜索字符串是否是最近搜索字符串的附加...以便您可以從上次搜索開始后續搜索(節省大量時間)。

我建議使用linq。

string x = "searchterm";
List<string> y = new List<string>();
List<string> Matches = y.Where(xo => xo.StartsWith(x)).ToList();

其中x是您的擊鍵搜索文本術語,y是您要搜索的字符串集合,而Matches是您的集合中的匹配項。

我用前100萬個素數測試了這個,這里是從上面改編的代碼:

        Stopwatch SW = new Stopwatch();
        SW.Start();
        string x = "2";
        List<string> y = System.IO.File.ReadAllText("primes1.txt").Split(' ').ToList();
        y.RemoveAll(xo => xo == " " || xo == "" || xo == "\r\r\n");
        List <string> Matches = y.Where(xo => xo.StartsWith(x)).ToList();
        SW.Stop();
        Console.WriteLine("matches: " + Matches.Count);
        Console.WriteLine("time taken: " + SW.Elapsed.TotalSeconds);
        Console.Read();

結果是:

比賽:77025

所用時間:0.4240604

當然這是針對數字的測試,我不知道linq之前是否轉換了值,或者數字是否有任何差異。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM