• 一个字符串搜索的Aho-Corasick算法


    Aho和Corasick对KMP算法(Knuth–Morris–Pratt algorithm)进行了改进,Aho-Corasick算法(Aho-Corasick algorithm)利用构建树,总时间复杂度是O(n)。原理图如下(摘自Aho-Corasick string matching in C#):

     

    Building of the keyword tree (figure 1 - after the first step, figure 2 - tree with the fail function)

    C#版本的实现代码可以从Aho-Corasick string matching in C#得到,也可以点击这里获得该算法的PDF文档。

    这是一个应用示例:

    预览图

    它能将载入的RTF文档中的搜索关键字高亮,检索速度较快,示例没有实现全字匹配,算法代码简要如下:

    [c-sharp] view plaincopy
     
    1. /* Aho-Corasick text search algorithm implementation 
    2.  *  
    3.  * For more information visit 
    4.  *      - http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf 
    5.  */  
    6. using System;  
    7. using System.Collections;  
    8. namespace EeekSoft.Text  
    9. {  
    10.     /// <summary>  
    11.     /// Interface containing all methods to be implemented  
    12.     /// by string search algorithm  
    13.     /// </summary>  
    14.     public interface IStringSearchAlgorithm  
    15.     {  
    16.         #region Methods & Properties  
    17.         /// <summary>  
    18.         /// Ignore case of letters  
    19.         /// </summary>  
    20.         bool IgnoreCase { getset; }  
    21.         /// <summary>  
    22.         /// List of keywords to search for  
    23.         /// </summary>  
    24.         string[] Keywords { getset; }  
    25.   
    26.         /// <summary>  
    27.         /// Searches passed text and returns all occurrences of any keyword  
    28.         /// </summary>  
    29.         /// <param name="text">Text to search</param>  
    30.         /// <returns>Array of occurrences</returns>  
    31.         StringSearchResult[] FindAll(string text);  
    32.         /// <summary>  
    33.         /// Searches passed text and returns first occurrence of any keyword  
    34.         /// </summary>  
    35.         /// <param name="text">Text to search</param>  
    36.         /// <returns>First occurrence of any keyword (or StringSearchResult.Empty if text doesn't contain any keyword)</returns>  
    37.         StringSearchResult FindFirst(string text);  
    38.         /// <summary>  
    39.         /// Searches passed text and returns true if text contains any keyword  
    40.         /// </summary>  
    41.         /// <param name="text">Text to search</param>  
    42.         /// <returns>True when text contains any keyword</returns>  
    43.         bool ContainsAny(string text);  
    44.         #endregion  
    45.     }  
    46.     /// <summary>  
    47.     /// Structure containing results of search   
    48.     /// (keyword and position in original text)  
    49.     /// </summary>  
    50.     public struct StringSearchResult  
    51.     {  
    52.         #region Members  
    53.         private int _index;  
    54.         private string _keyword;  
    55.         /// <summary>  
    56.         /// Initialize string search result  
    57.         /// </summary>  
    58.         /// <param name="index">Index in text</param>  
    59.         /// <param name="keyword">Found keyword</param>  
    60.         public StringSearchResult(int index, string keyword)  
    61.         {  
    62.             _index = index; _keyword = keyword;  
    63.         }  
    64.   
    65.         /// <summary>  
    66.         /// Returns index of found keyword in original text  
    67.         /// </summary>  
    68.         public int Index  
    69.         {  
    70.             get { return _index; }  
    71.         }  
    72.   
    73.         /// <summary>  
    74.         /// Returns keyword found by this result  
    75.         /// </summary>  
    76.         public string Keyword  
    77.         {  
    78.             get { return _keyword; }  
    79.         }  
    80.   
    81.         /// <summary>  
    82.         /// Returns empty search result  
    83.         /// </summary>  
    84.         public static StringSearchResult Empty  
    85.         {  
    86.             get { return new StringSearchResult(-1, ""); }  
    87.         }  
    88.         #endregion  
    89.     }  
    90.   
    91.     /// <summary>  
    92.     /// Class for searching string for one or multiple   
    93.     /// keywords using efficient Aho-Corasick search algorithm  
    94.     /// </summary>  
    95.     public class StringSearch : IStringSearchAlgorithm  
    96.     {  
    97.         #region Objects  
    98.         /// <summary>  
    99.         /// Tree node representing character and its   
    100.         /// transition and failure function  
    101.         /// </summary>  
    102.         class TreeNode  
    103.         {  
    104.             #region Constructor & Methods  
    105.             /// <summary>  
    106.             /// Initialize tree node with specified character  
    107.             /// </summary>  
    108.             /// <param name="parent">Parent node</param>  
    109.             /// <param name="c">Character</param>  
    110.             public TreeNode(TreeNode parent, char c)  
    111.             {  
    112.                 _char = c; _parent = parent;  
    113.                 _results = new ArrayList();  
    114.                 _resultsAr = new string[] { };  
    115.                 _transitionsAr = new TreeNode[] { };  
    116.                 _transHash = new Hashtable();  
    117.             }  
    118.   
    119.             /// <summary>  
    120.             /// Adds pattern ending in this node  
    121.             /// </summary>  
    122.             /// <param name="result">Pattern</param>  
    123.             public void AddResult(string result)  
    124.             {  
    125.                 if (_results.Contains(result)) return;  
    126.                 _results.Add(result);  
    127.                 _resultsAr = (string[])_results.ToArray(typeof(string));  
    128.             }  
    129.             /// <summary>  
    130.             /// Adds trabsition node  
    131.             /// </summary>  
    132.             /// <param name="node">Node</param>  
    133.             //public void AddTransition(TreeNode node)  
    134.             //{  
    135.             //    AddTransition(node, false);  
    136.             //}  
    137.             /// <summary>  
    138.             /// Adds trabsition node  
    139.             /// </summary>  
    140.             /// <param name="node">Node</param>  
    141.             /// <param name="ignoreCase">Ignore case of letters</param>  
    142.             public void AddTransition(TreeNode node, bool ignoreCase)  
    143.             {  
    144.                 if (ignoreCase) _transHash.Add(char.ToLower(node.Char), node);  
    145.                 else _transHash.Add(node.Char, node);  
    146.                 TreeNode[] ar = new TreeNode[_transHash.Values.Count];  
    147.                 _transHash.Values.CopyTo(ar, 0);  
    148.                 _transitionsAr = ar;  
    149.             }  
    150.             /// <summary>  
    151.             /// Returns transition to specified character (if exists)  
    152.             /// </summary>  
    153.             /// <param name="c">Character</param>  
    154.             /// <param name="ignoreCase">Ignore case of letters</param>  
    155.             /// <returns>Returns TreeNode or null</returns>  
    156.             public TreeNode GetTransition(char c, bool ignoreCase)  
    157.             {  
    158.                 if (ignoreCase)  
    159.                     return (TreeNode)_transHash[char.ToLower(c)];  
    160.                 return (TreeNode)_transHash[c];  
    161.             }  
    162.   
    163.             /// <summary>  
    164.             /// Returns true if node contains transition to specified character  
    165.             /// </summary>  
    166.             /// <param name="c">Character</param>  
    167.             /// <param name="ignoreCase">Ignore case of letters</param>  
    168.             /// <returns>True if transition exists</returns>  
    169.             public bool ContainsTransition(char c, bool ignoreCase)  
    170.             {  
    171.                 return GetTransition(c, ignoreCase) != null;  
    172.             }  
    173.             #endregion  
    174.             #region Properties  
    175.             private char _char;  
    176.             private TreeNode _parent;  
    177.             private TreeNode _failure;  
    178.             private ArrayList _results;  
    179.             private TreeNode[] _transitionsAr;  
    180.             private string[] _resultsAr;  
    181.             private Hashtable _transHash;  
    182.             /// <summary>  
    183.             /// Character  
    184.             /// </summary>  
    185.             public char Char  
    186.             {  
    187.                 get { return _char; }  
    188.             }  
    189.   
    190.             /// <summary>  
    191.             /// Parent tree node  
    192.             /// </summary>  
    193.             public TreeNode Parent  
    194.             {  
    195.                 get { return _parent; }  
    196.             }  
    197.   
    198.             /// <summary>  
    199.             /// Failure function - descendant node  
    200.             /// </summary>  
    201.             public TreeNode Failure  
    202.             {  
    203.                 get { return _failure; }  
    204.                 set { _failure = value; }  
    205.             }  
    206.   
    207.             /// <summary>  
    208.             /// Transition function - list of descendant nodes  
    209.             /// </summary>  
    210.             public TreeNode[] Transitions  
    211.             {  
    212.                 get { return _transitionsAr; }  
    213.             }  
    214.   
    215.             /// <summary>  
    216.             /// Returns list of patterns ending by this letter  
    217.             /// </summary>  
    218.             public string[] Results  
    219.             {  
    220.                 get { return _resultsAr; }  
    221.             }  
    222.             #endregion  
    223.         }  
    224.         #endregion  
    225.         #region Local fields  
    226.         /// <summary>  
    227.         /// Root of keyword tree  
    228.         /// </summary>  
    229.         private TreeNode _root;  
    230.         /// <summary>  
    231.         /// Keywords to search for  
    232.         /// </summary>  
    233.         private string[] _keywords;  
    234.         #endregion  
    235.         #region Initialization  
    236.         /// <summary>  
    237.         /// Initialize search algorithm (Build keyword tree)  
    238.         /// </summary>  
    239.         /// <param name="keywords">Keywords to search for</param>  
    240.         /// <param name="ignoreCase">Ignore case of letters (the default is false)</param>  
    241.         public StringSearch(string[] keywords, bool ignoreCase)  
    242.             : this(keywords)  
    243.         {  
    244.             IgnoreCase = ignoreCase;  
    245.         }  
    246.         /// <summary>  
    247.         /// Initialize search algorithm (Build keyword tree)  
    248.         /// </summary>  
    249.         /// <param name="keywords">Keywords to search for</param>  
    250.         public StringSearch(string[] keywords)  
    251.         {  
    252.             Keywords = keywords;  
    253.         }  
    254.   
    255.         /// <summary>  
    256.         /// Initialize search algorithm with no keywords  
    257.         /// (Use Keywords property)  
    258.         /// </summary>  
    259.         public StringSearch()  
    260.         { }  
    261.         #endregion  
    262.         #region Implementation  
    263.         /// <summary>  
    264.         /// Build tree from specified keywords  
    265.         /// </summary>  
    266.         void BuildTree()  
    267.         {  
    268.             // Build keyword tree and transition function  
    269.             _root = new TreeNode(null' ');  
    270.             foreach (string p in _keywords)  
    271.             {  
    272.                 // add pattern to tree  
    273.                 TreeNode nd = _root;  
    274.                 foreach (char c in p)  
    275.                 {  
    276.                     TreeNode ndNew = null;  
    277.                     foreach (TreeNode trans in nd.Transitions)  
    278.                     {  
    279.                         if (this.IgnoreCase)  
    280.                         {  
    281.                             if (char.ToLower(trans.Char) == char.ToLower(c)) { ndNew = trans; break; }  
    282.                         }  
    283.                         else  
    284.                         {  
    285.                             if (trans.Char == c) { ndNew = trans; break; }  
    286.                         }  
    287.                     }  
    288.                     if (ndNew == null)  
    289.                     {  
    290.                         ndNew = new TreeNode(nd, c);  
    291.                         nd.AddTransition(ndNew, this.IgnoreCase);  
    292.                     }  
    293.                     nd = ndNew;  
    294.                 }  
    295.                 nd.AddResult(p);  
    296.             }  
    297.             // Find failure functions  
    298.             ArrayList nodes = new ArrayList();  
    299.             // level 1 nodes - fail to root node  
    300.             foreach (TreeNode nd in _root.Transitions)  
    301.             {  
    302.                 nd.Failure = _root;  
    303.                 foreach (TreeNode trans in nd.Transitions) nodes.Add(trans);  
    304.             }  
    305.             // other nodes - using BFS  
    306.             while (nodes.Count != 0)  
    307.             {  
    308.                 ArrayList newNodes = new ArrayList();  
    309.                 foreach (TreeNode nd in nodes)  
    310.                 {  
    311.                     TreeNode r = nd.Parent.Failure;  
    312.                     char c = nd.Char;  
    313.                     while (r != null && !r.ContainsTransition(c, this.IgnoreCase)) r = r.Failure;  
    314.                     if (r == null)  
    315.                         nd.Failure = _root;  
    316.                     else  
    317.                     {  
    318.                         nd.Failure = r.GetTransition(c, this.IgnoreCase);  
    319.                         foreach (string result in nd.Failure.Results)  
    320.                             nd.AddResult(result);  
    321.                     }  
    322.                     // add child nodes to BFS list   
    323.                     foreach (TreeNode child in nd.Transitions)  
    324.                         newNodes.Add(child);  
    325.                 }  
    326.                 nodes = newNodes;  
    327.             }  
    328.             _root.Failure = _root;  
    329.         }  
    330.  
    331.         #endregion  
    332.         #region Methods & Properties  
    333.         /// <summary>  
    334.         /// Ignore case of letters  
    335.         /// </summary>  
    336.         public bool IgnoreCase  
    337.         {  
    338.             get;  
    339.             set;  
    340.         }  
    341.         /// <summary>  
    342.         /// Keywords to search for (setting this property is slow, because  
    343.         /// it requieres rebuilding of keyword tree)  
    344.         /// </summary>  
    345.         public string[] Keywords  
    346.         {  
    347.             get { return _keywords; }  
    348.             set  
    349.             {  
    350.                 _keywords = value;  
    351.                 BuildTree();  
    352.             }  
    353.         }  
    354.   
    355.         /// <summary>  
    356.         /// Searches passed text and returns all occurrences of any keyword  
    357.         /// </summary>  
    358.         /// <param name="text">Text to search</param>  
    359.         /// <returns>Array of occurrences</returns>  
    360.         public StringSearchResult[] FindAll(string text)  
    361.         {  
    362.             ArrayList ret = new ArrayList();  
    363.             TreeNode ptr = _root;  
    364.             int index = 0;  
    365.             while (index < text.Length)  
    366.             {  
    367.                 TreeNode trans = null;  
    368.                 while (trans == null)  
    369.                 {  
    370.                     trans = ptr.GetTransition(text[index], this.IgnoreCase);  
    371.                     if (ptr == _root) break;  
    372.                     if (trans == null) ptr = ptr.Failure;  
    373.                 }  
    374.                 if (trans != null) ptr = trans;  
    375.                 foreach (string found in ptr.Results)  
    376.                     ret.Add(new StringSearchResult(index - found.Length + 1, found));  
    377.                 index++;  
    378.             }  
    379.             return (StringSearchResult[])ret.ToArray(typeof(StringSearchResult));  
    380.         }  
    381.   
    382.         /// <summary>  
    383.         /// Searches passed text and returns first occurrence of any keyword  
    384.         /// </summary>  
    385.         /// <param name="text">Text to search</param>  
    386.         /// <returns>First occurrence of any keyword (or StringSearchResult.Empty if text doesn't contain any keyword)</returns>  
    387.         public StringSearchResult FindFirst(string text)  
    388.         {  
    389.             ArrayList ret = new ArrayList();  
    390.             TreeNode ptr = _root;  
    391.             int index = 0;  
    392.             while (index < text.Length)  
    393.             {  
    394.                 TreeNode trans = null;  
    395.                 while (trans == null)  
    396.                 {  
    397.                     trans = ptr.GetTransition(text[index], this.IgnoreCase);  
    398.                     if (ptr == _root) break;  
    399.                     if (trans == null) ptr = ptr.Failure;  
    400.                 }  
    401.                 if (trans != null) ptr = trans;  
    402.                 foreach (string found in ptr.Results)  
    403.                     return new StringSearchResult(index - found.Length + 1, found);  
    404.                 index++;  
    405.             }  
    406.             return StringSearchResult.Empty;  
    407.         }  
    408.   
    409.         /// <summary>  
    410.         /// Searches passed text and returns true if text contains any keyword  
    411.         /// </summary>  
    412.         /// <param name="text">Text to search</param>  
    413.         /// <returns>True when text contains any keyword</returns>  
    414.         public bool ContainsAny(string text)  
    415.         {  
    416.             TreeNode ptr = _root;  
    417.             int index = 0;  
    418.             while (index < text.Length)  
    419.             {  
    420.                 TreeNode trans = null;  
    421.                 while (trans == null)  
    422.                 {  
    423.                     trans = ptr.GetTransition(text[index], this.IgnoreCase);  
    424.                     if (ptr == _root) break;  
    425.                     if (trans == null) ptr = ptr.Failure;  
    426.                 }  
    427.                 if (trans != null) ptr = trans;  
    428.                 if (ptr.Results.Length > 0) return true;  
    429.                 index++;  
    430.             }  
    431.             return false;  
    432.         }  
    433.         #endregion  
    434.     }  
    435. }  

    示例下载页面:http://www.uushare.com/user/m2nlight/file/2722093

  • 相关阅读:
    Django基础篇
    知识梳理
    其他类题目
    CDN原理
    OpenStack
    云计算三种服务模式SaaS、PaaS和IaaS
    高并发架构
    Andrid Studio Gradle sync failed: A problem occurred configuring project ':app' 解决方法
    Android Studio 创建项目后“Cannot resolve symbol” 解决办法
    阅读之推荐系统
  • 原文地址:https://www.cnblogs.com/zwb7926/p/3469202.html
Copyright © 2020-2023  润新知