[英]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.