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文档中的搜索关键字高亮,检索速度较快,示例没有实现全字匹配,算法代码简要如下:

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

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

一个字符串搜索的Aho-Corasick算法的更多相关文章

  1. 多模字符串匹配算法-Aho–Corasick

    背景 在做实际工作中,最简单也最常用的一种自然语言处理方法就是关键词匹配,例如我们要对n条文本进行过滤,那本身是一个过滤词表的,通常进行过滤的代码如下 for (String document : d ...

  2. 【ToolGood.Words】之【StringSearch】字符串搜索——基于BFS算法

    字符串搜索中,BFS算法很巧妙,个人认为BFS算法效率是最高的. [StringSearch]就是根据BFS算法并优化. 使用方法: string s = "中国|国人|zg人|fuck|a ...

  3. C#算法之判断一个字符串是否是对称字符串

    记得曾经一次面试时,面试官给我电脑,让我现场写个算法,判断一个字符串是不是对称字符串.我当时用了几分钟写了一个很简单的代码. 这里说的对称字符串是指字符串的左边和右边字符顺序相反,如"abb ...

  4. 基于python 3.5 所做的找出来一个字符串中最长不重复子串算法

    功能:找出来一个字符串中最长不重复子串 def find_longest_no_repeat_substr(one_str): #定义一个列表用于存储非重复字符子串 res_list=[] #获得字符 ...

  5. 算法 - 给出一个字符串str,输出包含两个字符串str的最短字符串,如str为abca时,输出则为abcabca

    今天碰到一个算法题觉得比较有意思,研究后自己实现了出来,代码比较简单,如发现什么问题请指正.思路和代码如下: 基本思路:从左开始取str的最大子字符串,判断子字符串是否为str的后缀,如果是则返回st ...

  6. 算法:Manacher,给定一个字符串str,返回str中最长回文子串的长度。

    [题目] 给定一个字符串str,返回str中最长回文子串的长度 [举例] str="123", 1 str="abc1234321ab" 7 [暴力破解] 从左 ...

  7. 字符串模式匹配算法2 - AC算法

    上篇文章(http://www.cnblogs.com/zzqcn/p/3508442.html)里提到的BF和KMP算法都是单模式串匹配算法,也就是说,模式串只有一个.当需要在字符串中搜索多个关键字 ...

  8. 字符串混淆技术应用 设计一个字符串混淆程序 可混淆.NET程序集中的字符串

    关于字符串的研究,目前已经有两篇. 原理篇:字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串  实践篇:字符串反混淆实战 Dotfuscator 4.9 字符串加密技术应对策略 今天来 ...

  9. Aho - Corasick string matching algorithm

    Aho - Corasick string matching algorithm 俗称:多模式匹配算法,它是对 Knuth - Morris - pratt algorithm (单模式匹配算法) 形 ...

随机推荐

  1. html中 iframe子页面 与父页面之间的方法调用 ;

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. MVC学习笔记--IEnumerable的用法

    IEnumerable的用法 IEnumerable和IEnumerable<T>接口在.NET中是非常重要的接口,它允许开发人员定义foreach语句功能的实现 并支持非泛型方法的简单的 ...

  3. ACdream 1063 平衡树

    写的很丑的字典树.听王大神的话  需要改进. #include<stdio.h> #include<string.h> #include<math.h> #incl ...

  4. 常用类型转换 一.常用int和string类型转换

    常用类型转换 一.常用int类型转换1. int.parse(string) 这个类型只支持string类型 2.double doubleType = Int32.MaxValue + 1;   i ...

  5. POJ 2977 Box walking

    题目链接:http://poj.org/problem?id=2977 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 222 ...

  6. jquery获取li中的各项属性值attr

    发布新内容时的设计 默认显示一个按钮 如:发布按钮(放在h3字体里面)(鼠标上面时.显示发布到哪个模块下拉菜单发在li里面) $('#pup_model li , #pup_model h3').cl ...

  7. s=a+aa+aaa+aaaa+aa...aaaa

    main(){ int a,n,count=1; long int sn=0,tn=0; cout<<"input a and n:"; cin>>a> ...

  8. [ An Ac a Day ^_^ ] Codeforces Round #368 Div. 2 A B C

    昨天才回学校 刚好赶上CF所以就没写博客 不过还是水题了…… A. 比赛的时候被hack了 仔细读题才知道grey也算是黑白的 英语不好好伤心…… #include<stdio.h> #i ...

  9. Chapter 16_4 私密性

    在Lua面向对象编程的基础设计当中,没有提供私密性机制.但是可以用其他方法实现,从而获得对象的访问控制. 这种实现不常用,作为兴趣爱好,只做基本了解. 基本做法是:通过两个table来表示一个对象.一 ...

  10. 【Python爬虫实战--3】html写正则表达式

    以下是要爬虫的html内容: <div class="article block untagged mb15" id='qiushi_tag_113452216'> & ...