zoukankan      html  css  js  c++  java
  • Trie(C#)

    支持模糊搜索(非通配符)

    支持搜索结果优先级(首字母匹配、连续匹配有较高优先级

    TrieSearch.cs

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Threading.Tasks;
    
    // ReSharper disable UnusedAutoPropertyAccessor.Global
    // ReSharper disable MemberCanBePrivate.Global
    // ReSharper disable PossibleMultipleEnumeration
    namespace TrieCS
    {
        class ExampleContet
        {
            public string Name { get; set; }
    
            public string Content { get; set; }
    
            public static readonly ExampleContet[] Examples =
            {
                new ExampleContet
                {
                    Name = "app list",
                    Content = "YouJian Rili DianShi XiangCe Zoe YouTube Car ShiZhong HTCZhiNan LianXiRen PlayShangDian GuPiao BiaoGe ChaoJiYongHu WenDang Keep Seeder Sense6Toolbox XposedInstaller BaiDuNuoMi MeiTuan BaiDu BaiDuDiTu BaiDuLvYou BaiDuTieBa BiYingCiDian BoHao BuKaManHua ChiZi CunQian FanYi XiaMiYinYue"
                },
                new ExampleContet
                {
                    Name = "ins",
                    Content = "s sin dins"
                },
                new ExampleContet
                {
                    Name = "ab",
                    Content = "abc dab"
                },
                new ExampleContet
                {
                    Name = "blank",
                    Content = ""
                }
            };
        }
    
        /// <summary>
        /// Performance info
        /// </summary>
        public class PerfInfo
        {
            public double FilterOutMs { get; internal set; }
            public double TotalMs { get; internal set; }
    
            public override string ToString()
            {
                return String.Format("Total:{0:0.00000}ms, FilterAdd:{1:0.00000}ms",
                    TotalMs,
                    FilterOutMs);
            }
        }
    
        /// <summary>
        /// Grouped result of all <see cref="MatchInfo"/> with the same index.
        /// </summary>
        public class SearchResult : IComparable<SearchResult>
        {
            /// <summary>
            /// index in the <see cref="TrieSearch.Keywords"/> of a <see cref="TrieSearch"/>
            /// </summary>
            public int Index { get; set; }
            /// <summary>
            /// positions of matched chars in <see cref="Content"/>
            /// </summary>
            public SortedList<int, int> Positions { get; private set; }
            /// <summary>
            /// how excactly it matchs the query
            /// </summary>
            public int Priority { get; set; }
            /// <summary>
            /// 
            /// </summary>
            public string Content { get; set; }
    
            // ReSharper disable once UnusedMember.Global
            public string PositionString
            {
                get
                {
                    return String.Join(",", Positions.Values);
                }
            }
    
            public SearchResult()
            {
                Positions = new SortedList<int, int>();
            }
    
            public int CompareTo(SearchResult other)
            {
                return other.Priority.CompareTo(Priority);
            }
        }
    
        /// <summary>
        /// Result of an single matched char
        /// </summary>
        class MatchInfo
        {
            /// <summary>
            /// index in the <see cref="TrieSearch.Keywords"/> of a <see cref="TrieSearch"/>
            /// </summary>
            public int Index { get; set; }
            /// <summary>
            /// position the this char in a string
            /// </summary>
            public int Position { get; set; }
            /// <summary>
            /// 
            /// </summary>
            public int Priority { get; set; }
            /// <summary>
            /// the char content
            /// </summary>
            public char Data { get; set; }
    
            public override string ToString()
            {
                return String.Format("[{0}.{1}]", Index, Position);
            }
        }
    
        /// <summary>
        /// Trie node
        /// </summary>
        class Node
        {
            /// <summary>
            /// Char content. Only for data visualization.
            /// </summary>
            public char Data { get; set; }
            /// <summary>
            /// All <see cref="MatchInfo"/> of this node.
            /// </summary>
            public List<MatchInfo> Infos { get; private set; }
    
            public Node()
            {
                Infos = new List<MatchInfo>();
            }
            public override string ToString()
            {
                return Data.ToString(CultureInfo.InvariantCulture);
            }
        }
    
        /// <summary>
        /// 
        /// </summary>
        class TrieSearch
        {
            /// <summary>
            /// Performance info of the last search.
            /// </summary>
            public PerfInfo LastPerfInfo { get; private set; }
    
            /// <summary>
            /// All nodes in this trie.
            /// </summary>
            public Dictionary<char, Node> Nodes { get; private set; }
    
            private string[] _keywords;
            /// <summary>
            /// the contents from which you may want to search.
            /// <para/>setting this value will immidietly build the trie.
            /// </summary>
            public string[] Keywords
            {
                get { return _keywords; }
            }
    
            /// <summary>
            /// Build the trie. A simple async wrapper for <see cref="BuildTree"/>
            /// </summary>
            /// <param name="contents"></param>
            /// <returns></returns>
            public Task BuildTreeAsync(string[] contents)
            {
                return Task.Run(() => BuildTree(contents));
            }
    
            /// <summary>
            /// Search. A simple async wrapper for <see cref="Search"/>
            /// </summary>
            /// <param name="query"></param>
            /// <returns>Matched results</returns>
            public Task<SearchResult[]> SearchAsync(string query)
            {
                return Task.Run<SearchResult[]>(() => Search(query));
            }
    
            /// <summary>
            /// build the tree.
            /// </summary>
            /// <param name="index">index of the keyword in <see cref="Keywords"/></param>
            public void BuildTree(string[] contents)
            {
                _keywords = contents;
                Nodes = new Dictionary<char, Node>();
                for (var i = 0; i < contents.Length; i++)
                {
                    string keyword = contents[i];
                    for (var j = 0; j < keyword.Length; j++)
                    {
                        var c = keyword[j];
                        if (!Char.IsLetter(c)) continue;
                        var info = new MatchInfo
                        {
                            Index = i,
                            Position = j,
                            Priority = j == 0 ? 3 : 1,  // initial char has a higher priority
                            Data = c,
                        };
    
                        // Capital char has a higher priority.
                        if (Char.IsUpper(c))
                            info.Priority += 2;
    
                        // Only lower char is accepted.
                        c = Char.ToLower(c);
    
                        Node newNode;
                        if (!Nodes.TryGetValue(c, out newNode))
                        {
                            newNode = new Node { Data = c };
                            Nodes[c] = newNode;
                        }
    
                        // Record this match info.
                        newNode.Infos.Add(info);
                    }
                }
            }
    
            /// <summary>
            /// Search
            /// </summary>
            /// <param name="query"></param>
            /// <returns>Matched results</returns>
            public SearchResult[] Search(string query)
            {
                LastPerfInfo = new PerfInfo();
    
                if (Keywords.Length == 0)
                    return new SearchResult[0];
    
                var timer = HiPerfTimer.StartNew();
                var lastMatchedPositions = new Dictionary<int, int>();
                var resultList = new Dictionary<int, SearchResult>();
                var isInOrder = new Dictionary<int, bool>();
    
                for (int pos = 0; pos < query.Length; pos++)
                {
                    var c = query[pos];
                    Node node;
                    if (!Nodes.TryGetValue(c, out node))
                    {
                        resultList.Clear();
                        break;
                    }
    
                    FilterOut(resultList, node);
    
                    isInOrder = new Dictionary<int, bool>();
    
                    node.Infos.ForEach(info =>
                    {
                        var index = info.Index;
                        if (pos == 0)
                        {
                            if (!lastMatchedPositions.ContainsKey(index))
                                lastMatchedPositions[index] = info.Position;
                            isInOrder[index] = true;
                        }
                        else
                        {
                            if (!resultList.ContainsKey(index))
                            {
                                resultList.Remove(index);
                                return;
                            }
                            else if (isInOrder.ContainsKey(index) && isInOrder[index])
                            {
                                return;
                            }
                            else
                            {
                                if (info.Position > lastMatchedPositions[index])
                                {
                                    isInOrder[index] = true;
                                    lastMatchedPositions[index] = info.Position;
                                    Debug.WriteLine("pos of {0} is now {1}", index, info.Position);
                                }
                                else
                                {
                                    isInOrder[index] = false;
                                }
                            }
                        }
                            
                        AddToResult(resultList, info, index);
                    });
                    // filter out items not in order.
                    foreach (var item in isInOrder.Where(_ => !_.Value).Select(_ => _.Key))
                    {
                        resultList.Remove(item);
                    }
                }
    
                // other procedures
                var resultArray = PostProcess(resultList);
    
                timer.Stop();
                LastPerfInfo.TotalMs = timer.Duration;
    
                return resultArray;
            }
    
            /// <summary>
            /// Remove items, which not appears in node.Infos, in resultList
            /// </summary>
            /// <param name="resultList"></param>
            /// <param name="node"></param>
            private static void FilterOut(Dictionary<int, SearchResult> resultList, Node node)
            {
                var lookup = node.Infos.ToLookup(_ => _.Index);
                var dif = resultList.Select(_ => _.Key).Where(_ => !lookup.Contains(_)).ToList();
                dif.ForEach(_ => resultList.Remove(_));
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="resultList"></param>
            /// <returns></returns>
            private static SearchResult[] PostProcess(Dictionary<int, SearchResult> resultList)
            {
                var resultArray = resultList.Values.ToArray();
                foreach (var item in resultArray)
                {
                    // increase priority if continues.
                    if (Continues(item.Positions.Values))
                        item.Priority += 4;
                }
                return resultArray;
            }
    
            /// <summary>
            /// Add <see cref="MatchInfo"/> to resultList
            /// </summary>
            /// <param name="resultList"></param>
            /// <param name="info"></param>
            /// <param name="index"></param>
            private void AddToResult(Dictionary<int, SearchResult> resultList, MatchInfo info, int index)
            {
                SearchResult sr;
                if (!resultList.TryGetValue(info.Index, out sr))
                {
                    sr = new SearchResult
                    {
                        Index = index,
                        Content = Keywords[index]
                    };
                    resultList[index] = sr;
                }
                if (!sr.Positions.ContainsKey(info.Position))
                {
                    sr.Positions.Add(info.Position, info.Position);
                    if (info.Priority > sr.Priority)
                        sr.Priority = info.Priority;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="list"></param>
            /// <returns></returns>
            private static bool Continues(IEnumerable<int> list)
            {
                var last = list.ElementAt(0);
                var threshold = 3;
                foreach (var item in list.Skip(1))
                {
                    if (item != last + 1)
                    {
                        threshold = 3;
                        last = item;
                        continue;
                    }
                    last = item;
                    threshold--;
                    if (threshold == 0)
                        return true;
                }
                return false;
            }
        }
    }
    // ReSharper restore PossibleMultipleEnumeration
    // ReSharper restore MemberCanBePrivate.Global
    // ReSharper restore UnusedAutoPropertyAccessor.Global

    HiPerfTimer

    /* High precision timer class
     *   - http://www.eggheadcafe.com/articles/20021111.asp
     * 
     *  (Thanks to the author!)
     */
    using System;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Threading;
    
    namespace TrieCS
    {
        public class HiPerfTimer
        {
            [DllImport("Kernel32.dll")]
            private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
            [DllImport("Kernel32.dll")]
            private static extern bool QueryPerformanceFrequency(out long lpFrequency);
            private long startTime;
            private long stopTime;
            private long freq;
            /// <summary>
            /// ctor
            /// </summary>
            public HiPerfTimer()
            {
                startTime = 0;
                stopTime = 0;
                freq = 0;
                if (QueryPerformanceFrequency(out freq) == false)
                {
                    throw new Win32Exception(); // timer not supported
                }
            }
    
            public static HiPerfTimer StartNew()
            {
                var t = new HiPerfTimer();
                t.Start();
                return t;
            }
    
            /// <summary>
            /// Start the timer
            /// </summary>
            /// <returns>long - tick count</returns>
            public long Start()
            {
                QueryPerformanceCounter(out startTime);
                return startTime;
            }
            /// <summary>
            /// Stop timer 
            /// </summary>
            /// <returns>long - tick count</returns>
            public long Stop()
            {
                QueryPerformanceCounter(out stopTime);
                return stopTime;
            }
            /// <summary>
            /// Return the duration of the timer (in milliseconds)
            /// </summary>
            /// <returns>double - duration</returns>
            public double Duration
            {
                get
                {
                    return (double)(stopTime - startTime) / (double)freq * 1000.0;
                }
            }
            /// <summary>
            /// Frequency of timer (no counts in one second on this machine)
            /// </summary>
            ///<returns>long - Frequency</returns>
            public long Frequency
            {
                get
                {
                    QueryPerformanceFrequency(out freq);
                    return freq;
                }
            }
        }
    }
    View Code

     

  • 相关阅读:
    asp.net判断访问者是否来自移动端
    Chrome模拟手机浏览器(iOS/Android)的三种方法,亲测无误!
    html有序列表和无序列表
    ASP.Net 类(CS)文件怎样获取Web应用程序的路径
    Web Service无法加载协定为“ServiceReference1.xxxxxx”的终结点配置部分,因为找到了该协定的多个终结点配置。请按名称指示首选的终结点配置部分
    Web serviser请求通道在等待 00:00:59.6479648 以后答复时超时。增加传递给请求调用的超时值,或者增加绑定上的 SendTimeout 值。分配给此操作的时间可能是更长超时的一部分。
    ASP.NET C# 文件下载
    PHP 取网页变量
    PHP 乘法口诀表
    Apache
  • 原文地址:https://www.cnblogs.com/ornithopter/p/3773119.html
Copyright © 2011-2022 走看看