原文地址:http://blog.11034.org/2012-07/trie_in_php.html

项目需求,要做敏感词过滤,对于敏感词本身就是一个CRUD的模块很简单,比较麻烦的就是对各种输入的敏感词检测了。用Trie树来实现是比较通用的一种办法吧,之前一直没机会用过这种数据结构,正好试着写了一下。

因为用PHP实现,关联数组用的很舒服。第一个要解决的是字符集的问题,如果在Java中就比较好办统一的Unicode,在PHP中因为常用 UTF-8字符集,默认有1-4个字节不同的长度来表示一个字符,于是写了个Util类来将普通的UTF-8字符串转换成字符数组,每一个元素是一个 UTF-8串形成的字符。这一点比较容易实现的,根据UTF-8字符集的格式而来就好。

  1. public static function get_chars($utf8_str){
  2. $s = $utf8_str;
  3. $len = strlen($s);
  4. if($len == 0) return array();
  5. $chars = array();
  6. for($i = 0;$i < $len;$i++){
  7. $c = $s[$i];
  8. $n = ord($c);
  9. if(($n >> 7) == 0){ //0xxx xxxx, asci, single
  10. $chars[] = $c;
  11. }
  12. else if(($n >> 4) == 15){ //1111 xxxx, first in four char
  13. if($i < $len - 3){
  14. $chars[] = $c.$s[$i + 1].$s[$i + 2].$s[$i + 3];
  15. $i += 3;
  16. }
  17. }
  18. else if(($n >> 5) == 7){ //111x xxxx, first in three char
  19. if($i < $len - 2){
  20. $chars[] = $c.$s[$i + 1].$s[$i + 2];
  21. $i += 2;
  22. }
  23. }
  24. else if(($n >> 6) == 3){ //11xx xxxx, first in two char
  25. if($i < $len - 1){
  26. $chars[] = $c.$s[$i + 1];
  27. $i++;
  28. }
  29. }
  30. }
  31. return $chars;
  32. }
   

字符单位确认以后,就是写Trie树了。简单的算法,从根路径开始给每个字符建一个关联数组,当字符串结束的时候,用一个null表示结尾。

删除一个串,只要找到串中任意一个字符的子元素数量为1,就表示只有这个串了,整个删除就好了;若子元素数量大于1,则继续根据字符找下去,直到末尾的null。

查找一个串(完全匹配),一直根据字符找到null为止就表明存在,任一字符不存在就表明串不存在。

验证一个长串是否含有任一串,这边算法比较挫,按照每个字符开始都在Trie树种搜索一遍,走的回头路比较多,复杂度有O(n * m),n为长串长度,m为Trie树深度,不过因为中文Trie树深度很浅,勉强还过得去(英文字符串深度很长)。

然后因为PHP没有全局缓存的机制,每次都要从数据库中读取全部的敏感词,然后建立Trie树再去匹配串的话太麻烦了,采取的办法是将Trie内部 的关联数组序列化后直接保存在数据库中,每次只要读取这条数据,然后反序列化,Trie树就回来了。当然进行串的插入和删除,将更新这个序列化数据。

可改进的地方:

  1. 当某一条路径只有这个串即关联数组数量为1时,可以压缩子树
  2. 改进Trie树为AC自动机,即每个节点都添加一个失败指针,指向匹配失败后回到树的哪个节点,这样就仅仅是O(n)的复杂度了。建树的过程比较复杂,对每个插入的串的子串进行处理,运行时查询的效率非常高

贴代码:

  1. class TrieTree{
  2.  
  3. public $tree = array();
  4.  
  5. public function insert($utf8_str){
  6. $chars = &UTF8Util::get_chars($utf8_str);
  7. $chars[] = null; //串结尾字符
  8. $count = count($chars);
  9. $T = &$this->tree;
  10. for($i = 0;$i < $count;$i++){
  11. $c = $chars[$i];
  12. if(!array_key_exists($c, $T)){
  13. $T[$c] = array(); //插入新字符,关联数组
  14. }
  15. $T = &$T[$c];
  16. }
  17. }
  18.  
  19. public function remove($utf8_str){
  20. $chars = &UTF8Util::get_chars($utf8_str);
  21. $chars[] = null;
  22. if($this->_find($chars)){ //先保证此串在树中
  23. $chars[] = null;
  24. $count = count($chars);
  25. $T = &$this->tree;
  26. for($i = 0;$i < $count;$i++){
  27. $c = $chars[$i];
  28. if(count($T[$c]) == 1){ //表明仅有此串
  29. unset($T[$c]);
  30. return;
  31. }
  32. $T = &$T[$c];
  33. }
  34. }
  35. }
  36.  
  37. private function _find(&$chars){
  38. $count = count($chars);
  39. $T = &$this->tree;
  40. for($i = 0;$i < $count;$i++){
  41. $c = $chars[$i];
  42. if(!array_key_exists($c, $T)){
  43. return false;
  44. }
  45. $T = &$T[$c];
  46. }
  47. return true;
  48. }
  49.  
  50. public function find($utf8_str){
  51. $chars = &UTF8Util::get_chars($utf8_str);
  52. $chars[] = null;
  53. return $this->_find($chars);
  54. }
  55.  
  56. public function contain($utf8_str, $do_count = 0){
  57. $chars = &UTF8Util::get_chars($utf8_str);
  58. $chars[] = null;
  59. $len = count($chars);
  60. $Tree = &$this->tree;
  61. $count = 0;
  62. for($i = 0;$i < $len;$i++){
  63. $c = $chars[$i];
  64. if(array_key_exists($c, $Tree)){ //起始字符匹配
  65. $T = &$Tree[$c];
  66. for($j = $i + 1;$j < $len;$j++){
  67. $c = $chars[$j];
  68. if(array_key_exists(null, $T)){
  69. if($do_count){
  70. $count++;
  71. }
  72. else{
  73. return true;
  74. }
  75. }
  76. if(!array_key_exists($c, $T)){
  77. break;
  78. }
  79. $T = &$T[$c];
  80. }
  81. }
  82. }
  83. if($do_count){
  84. return $count;
  85. }
  86. else{
  87. return false;
  88. }
  89. }
  90.  
  91. public function contain_all($str_array){
  92. foreach($str_array as $str){
  93. if($this->contain($str)){
  94. return true;
  95. }
  96. }
  97. return false;
  98. }
  99.  
  100. public function export(){
  101. return serialize($this->tree);
  102. }
  103.  
  104. public function import($str){
  105. $this->tree = unserialize($str);
  106. }
  107.  
  108. }
   

[转载]敏感词过滤,PHP实现的Trie树的更多相关文章

  1. 转,敏感词过滤,PHP实现的Trie树

    原文地址:http://blog.11034.org/2012-07/trie_in_php.html 项目需求,要做敏感词过滤,对于敏感词本身就是一个CRUD的模块很简单,比较麻烦的就是对各种输入的 ...

  2. DFA和trie特里实现敏感词过滤(python和c语言)

    今天的项目是与完成python开展,需要使用做关键词检查,筛选分类,使用前c语言做这种事情.有了线索,非常高效,内存小了,检查快. 到达python在,第一个想法是pip基于外观的c语言python特 ...

  3. [原创] Trie树 php 实现敏感词过滤

    目录 背景 简介 存储结构 PHP 其他语言 字符串分割 示例代码 php 优化 缓存字典树 常驻服务 参考文章 背景 项目中需要过滤用户发送的聊天文本, 由于敏感词有将近2W条, 如果用 str_r ...

  4. 用php实现一个敏感词过滤功能

    周末空余时间撸了一个敏感词过滤功能,下边记录下实现过程. 敏感词,一方面是你懂的,另一方面是我们自己可能也要过滤一些人身攻击或者广告信息等,具体词库可以google下,有很多. 过滤敏感词,使用简单的 ...

  5. 浅析敏感词过滤算法(C++)

    为了提高查找效率,这里将敏感词用树形结构存储,每个节点有一个map成员,其映射关系为一个string对应一个TreeNode. STL::map是按照operator<比较判断元素是否相同,以及 ...

  6. 转:鏖战双十一-阿里直播平台面临的技术挑战(webSocket, 敏感词过滤等很不错)

    转自:http://www.infoq.com/cn/articles/alibaba-broadcast-platform-technology-challenges 鏖战双十一-阿里直播平台面临的 ...

  7. Jsp敏感词过滤

    Jsp敏感词过滤 大部分论坛.网站等,为了方便管理,都进行了关于敏感词的设定. 在多数网站,敏感词一般是指带有敏感政治倾向(或反执政党倾向).暴力倾向.不健康色彩的词或不文明语,也有一些网站根据自身实 ...

  8. PHP实现敏感词过滤系统

    PHP实现敏感词过滤系统 安装说明 安装PHP扩展 trie_filter,安装教程 http://blog.41ms.com/post/39.html 安装PHP扩展 swoole,安装教程 htt ...

  9. 转:Java实现敏感词过滤

    敏感词.文字过滤是一个网站必不可少的功能,如何设计一个好的.高效的过滤算法是非常有必要的.前段时间我一个朋友(马上毕业,接触编程不久)要我帮他看一个文字过滤的东西,它说检索效率非常慢.我把它程序拿过来 ...

随机推荐

  1. 在webpack中配置vue.js

    在webpack中配置vue.js 这里有两种在webpack中配置vue.js的方法,如下: 1.在main.js中引入vue的包: index.html: <!DOCTYPE html> ...

  2. linux 命令 后台运行

    转载 1.在下达的命令后面加上&,就可以使该命令在后台进行工作,这样做最大的好处就是不怕被ctrl+c这个中断指令所中断. 2. 那大家可能又要问了,在后台执行的程序怎么使它恢复到前台来运行呢 ...

  3. 安装Elasticsearch5.0 部署Head插件

    部署5.0版本的ES 5.0版本的ES跟之前的版本最大的不同之处就是多了很多环境的校验,比如jdk,max-files等等. 设置内核参数 vi /etc/sysctl.conf # 增加下面的内容 ...

  4. Android Studio打开项目提示找不到sdk路径的问题。

    问题如图: 这是由于所打开的项目不是本机创建的,所使用的sdk路径不一致所导致. 解决方案: 打开项目所在目录,找到local.properties文件并打开,发现sdk.dir=D\:\\Andro ...

  5. 为android游戏开发-准备的地图编辑器-初步刷地图

    采用多文理混合,单页面支持8张文理进行刷绘

  6. 开源项目Log4j

    一:Log4j入门简介学习 Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务器.NT的事件记录器.UNIX ...

  7. VF

    VF 描述 Vasya is the beginning mathematician. He decided to make an important contribution to the scie ...

  8. [学习笔记] Miller-Rabin质数测试 & Pollard-Rho质因数分解

    目录 Miller-Rabin质数测试 & Pollard-Rho质因数分解 Miller-Rabin质数测试 一些依赖的定理 实现以及正确率 Pollard-Rho质因数分解 生日悖论与生日 ...

  9. Hive中自定义Map/Reduce示例 In Python

    Hive支持自定义map与reduce script.接下来我用一个简单的wordcount例子加以说明.使用Python开发(如果使用Java开发,请看这里). 开发环境: python:2.7.5 ...

  10. sendsms短信验证功能实现代码

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name ...