我是擅(倾)长(向)把一篇文章写成杂文的。毕竟,写博客记录生活点滴,比不得发 paper,要求字斟句酌八股结构到位;风格偏杂文一点,也是没人拒稿的。这么说来,arxiv 就好比是 paper 世界的博客,整了篇论文,管他三七二十一,放到 arxiv 上自嗨一番(如果不是自鸣得意的话)再说……

话说在优酷看了个电影《北京爱情故事》。记得当初电视剧的主题曲满大街放的时候,我还不知道有这么一电视剧;机缘巧合,某次宿舍里见朋友在看,才跟着一起看了两集,觉得不错,不过,之后自己也没再看过。今儿晚上看了同名电影,整体感受是不错,低调不煽情,也没有过分矫情,某些包袱抖得很好,情节串联的还行,有些场景有些小触动,具体在此不表……

切入正题。

上周六折腾了一个名字,Sakuc,扩展开来叫做 Swiss Army Knife using C,中文名可姑且叫做「瑞士军刀C系列」…… 主要目的,是完成一些基本的数据结构和常见算法,供平时鼓捣一些东东时方便一点。

众所周知,相对于更加现代的语言如 Python Java 等等,C 的标准库是出了名的短小精悍(也叫「功能稀缺」);当然这是有原因的,毕竟 C 和硬件结合相对紧密,保持短小精悍可以更好的兼顾可移植性和底层开发便利;加上各种委员会们,可能也不太希望把 C 整成另一个 C++,你懂的。总之,C 的世界里,一直都缺乏(被标准化的)常见数据结构和算法实现。

当然,非标准化的,一直都不缺。比如大名鼎鼎的 Gnome 项目衍生出来的 glib,某个多年未更新但质量似乎很高的 c-algorithms 项目等等。那重复造轮子的意义何在?嗯,猛戳此处看 stack exchange 创始人的经典长文。

废话一箩筐之后,我们来说一说多模式字符串匹配的经典算法,Aho-Corasick。最为直接的理解,请参考 wikipedia 的解释。所谓的 suffix link 即蓝色箭头,就好比是 KMP 单模式字符串匹配算法里的回退跳转,dict suffix link 则是多模式匹配里特有的「单次匹配 => 多个关键字」(如 bca 同时匹配了 bca 和 a - 不考虑之前已经匹配了的 bc 关键字)。

听着比较复杂对吗?让我们说的简单一点。
#  假设现在没有什么 aho-corasick 也没有什么 kmp 算法,就给你一个需求「需要在长文本里,同时匹配多个关键字」,不妨动脑筋思考一番。(两分钟过去了)
#  如果没想出来,那再具体一点:需要在长文本里,匹配「我去」「我靠」「我擦」三个关键字。(两分钟过去了)
#  如果还没想出来,那咱本着治病救人的态度,再把需求具体一点:请在文本「哈哈哇塞这次还想不出来就信了你的邪啦」,匹配上述三个关键字。

已经想到解法了吧?三个关键字共有的「我」前缀,在匹配文本里压根就没出现,所以根本不会有任何关键字能够得到匹配;换句话说,将这个思路通用化,即是,通过关键字列表,得到一个先验信息,如构建一颗前缀树,再根据输入流,做依次匹配。

一下子,感觉很简单了对吗?没错,从最初碰到「如果要在一篇文章中匹配十万个关键词怎么办」的帖子,到得到上面的想法,我只用了 5 分钟左右。但是,真正从这个想法,到最终彻底形成清晰的思路(也包括查阅 wikipedia 的解释,相关的 Trie wikipedia 和这篇博客),足足花了一晚上…… 最终的实现,则是花掉了将近一整天时间,正所谓「知易行难」…… 囧

先上接口设计。接口说明和使用,可以分别参考头文件单元测试例子。有兴趣的,可以读一读算法实现部分。

int sakuc_multi_pattern_build_search_automaton
(struct trie_node **root, const char *keywords[], size_t num,
size_t fifo_init_size); int sakuc_multi_pattern_find_node
(struct trie_node *root, const char *keyword,
struct trie_node **matched); int sakuc_multi_pattern_search
(const struct trie_node *search_db,
enum sakuc_mpm_search_mode search_mode,
const char *input, size_t len,
size_t *matched_pos_suffix, const char **matched_keyword); int sakuc_multi_pattern_destroy_search_automaton
(struct trie_node *root, size_t fifo_init_size);

最后说一下实际字符串搜索匹配接口(上面的第三个接口)的设计。自动机是肯定要指定的,即 search_db,输入的字符流也是要指定的,即 input 和 len。接着自然而然的问题是,如何返回匹配到的结果?直接 printf 输出到终端,显然不靠谱;将所有匹配的结果,存储在一个容器里,想法固然没问题,但不够灵活,一方面,是容器类型必须提前指定,另一方面,万一使用 api 的人只想匹配到了任何一个关键字就中止搜索怎么办(比如在十万个敏感词里,只要匹配到任何一个,就拒绝提交)。

最后,我选择了类似 C++ 的迭代器方案:每次仅给出单个匹配结果,即 api 的最后两个参数;根据函数返回值 0 / 1 / -1 来确定究竟是已经到达了输入字符流结尾、还是匹配 ok 等待下一次迭代、还是参数等出错了;根据函数的 search_mode 参数,决定重启新的搜索,或是继续上一次搜索。

关于这里「迭代器」的实现,有兴趣的可以参考如下的宏。估计很多人看这里会很奇怪:怎么又是宏又是 goto 操作…… 嘿嘿,没错,看着是有一点「冒权威之大不韪」的感觉,但是,善用语言特性,仔细处理边界条件,是能够让生活更美好的…… 囧 做为参考链接,可以考察一下 Contiki 操作系统里的 protoThread 库,更是将 C 的这种「接近汇编」的特色用到了极致。

// only used in function @sakuc_multi_pattern_search
#define _init_keyword_iterate() static int _continue = FALSE #define _continue_last_iter() do { \
_continue = TRUE; \
goto sakuc_mpm_search_iter_continue; \
} while (__LINE__ == -1) #define _get_ready_for_return() _continue = FALSE #define _return_and_wait_for_next_iter() do { \
sakuc_mpm_search_iter_continue: \
if (!_continue) \
return 1; \
} while (__LINE__ == -1)

以上。

「2014-3-18」multi-pattern string match using aho-corasick的更多相关文章

  1. 「模拟8.18」字符串(卡特兰数)·乌鸦喝水(树状数组,二分)·所驼门王的宝藏(tarjan,拓扑)

    最近好颓啊,所以啥都做不出来 简单说一下这次考试,分机房了,还分不同考卷,果然我还是留在二机房的蒟蒻, 大概也只有这样的简单题,才能勉强水个rank 3吧........ 其实不必管在哪个机房,努力便 ...

  2. 「10.17-10.18」liu_runda’s模拟

    暂咕 $day1$ A. 位运算 分类讨论,贡献分离. B. 集合论 维护类似时间戳的东西 C. 连连看 考场思路太局限了,考虑容斥. 我们可以看出两个方块能作出贡献,实际上是一个极大联通块(白块)所 ...

  3. 「查缺补漏」巩固你的Redis知识体系

    Windows Redis 安装 链接: https://pan.baidu.com/s/1MJnzX_qRuNXJI09euzkPGA 提取码: 2c6w 复制这段内容后打开百度网盘手机App,操作 ...

  4. [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?

    [译]聊聊C#中的泛型的使用(新手勿入)   写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发 ...

  5. [转帖]「知乎知识库」— 5G

    「知乎知识库」— 5G 甜草莓 https://zhuanlan.zhihu.com/p/55998832 ​ 通信 话题的优秀回答者 已关注 881 人赞同了该文章 谢 知识库 邀请~本文章是几个答 ...

  6. 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!

    ❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...

  7. 企业运营对 DevOps 的「傲慢与偏见」

    摘要:出于各种原因,并非所有人都信任 DevOps .有些人觉得 DevOps 只不过给开发者改善产品提供了一个途径而已,还有的人觉得 DevOps 是一堆悦耳的空头支票,甚至有人认为 DevOps ...

  8. 「造个轮子」——cicada 设计一个配置模块

    前言 在前两次的 cicada 版本中其实还不支持读取配置文件,比如对端口.路由的配置. 因此我按照自己的想法创建了一个 issue ,也收集到了一些很不错的建议. 最终其实还是按照我之前的想法来做了 ...

  9. 「造个轮子」——cicada 源码分析

    前言 两天前写了文章<「造个轮子」--cicada(轻量级 WEB 框架)> 向大家介绍了 cicada 之后收到很多反馈,也有许多不错的建议. 同时在 GitHub 也收获了 80 几颗 ...

随机推荐

  1. Host 'XXX' is not allowed to connect to this MySQL server 解决方案/如何开启MySQL的远程帐号

    www.cnblogs.com/zhangzhu/archive/2013/08/22/3274831.html 如何开启MySQL的远程帐号-1)首先以 root 帐户登陆 MySQL 在 Wind ...

  2. Java线程的5个使用技巧

    萝卜白菜各有所爱.像小编我就喜欢Java.学无止境,这也是我喜欢它的一个原因.日常工作中你所用到的工具,通常都有些你从来没有了解过的东西,比方说某个方法或者是一些有趣的用法.比如说线程.没错,就是线程 ...

  3. Xilium.CefGlue CEF Chrome 自动上传文件不弹出对话框 CefDialogHandler

    关键代码如下,如何使用Handler,还不清楚的请继续搜索 internal sealed class WyzCefDialogHandler : CefDialogHandler    {      ...

  4. C# DataGridView控件清空数据完美解决方法

    C# DataGridView控件绑定数据后清空数据在清除DataGridview的数据时: 1.DataSource为NULL(DataGridView.DataSource= null;)这样会将 ...

  5. uvm - dut

    module dut(clk, rst_n, rxd, rx_dv, txd, tx_en); input clk; input rst_n; :] rxd; input rx_dv; :] txd; ...

  6. Sublime Text3 (转) 配置 以及快捷键配置

    一.介绍 Sublime Text 是一款较新的编辑器,它轻量.简洁.高效,良好的扩展性以及跨平台等特性,使得越来越多的开发人员喜爱.它是一款收费的商业软件,但可以免费无限制无限期的试用,只会偶尔提醒 ...

  7. 关于spring boot jar包与war包的问题

    此文为转载:http://mrlee23.iteye.com/blog/2047968 在开发调试完成之后,可以将应用打成JAR包的形式,在Eclipse中可以直接使用Maven插件的package命 ...

  8. 【随笔】Photoshop简单切图

    一.首先准备一张需要切割的图: 二.打开Photoshop,在ps里打开这张图片: 三.在界面的左边选择切片工具: 四.根据需要,鼠标点住上方和左边的标尺拖动,给图片加上分割辅助线: 五.用切片工具点 ...

  9. postgresql+slony-i安装配置主从

    slon软件下载地址:slony1-1.2.6 http://slony.info/downloads/1.2/source/ postgresql下载地址: http://www.postgresq ...

  10. SQL Server已提交读快照隔离级别的设置

    如果要把SQL Server数据库事务隔离级别设置为已提交读快照隔离 如果直接运行下面的语句: ALTER Database [mydbname] SET READ_COMMITTED_SNAPSHO ...