AC自动机处理敏感词 php代码实现
最近公司接到一个敏感词需求,但是要通过文章去筛选敏感词。如果只通过接口去处理是性能不高。
我记得以前做的一个项目是使用AC自动机,处理敏感词的。以下是我百度的一个人写的博客。
class Node { public $value; // 节点值 public $is_end = false; // 是否为结束--是否为某个单词的结束节点 public $childNode = array(); // 子节点 public $fail = 0; // 失败指针 public $failIndex = 0; // 失败数组指针 public $trie = 0; // 结构树层级 public $parent = false; // 父节点 // 添加孩子节点--注意:可以不为引用函数,因为PHP对象赋值本身就是引用赋值 public function &addChildNode($value, $is_end = false) { $node = $this->searchChildNode($value); if (empty($node)) { // 不存在节点,添加为子节点 $node = new Node(); $node->value = $value; $node->parent = &$this; $node->trie = $this->trie + 1; $this->childNode[] = $node; } if (!$node->is_end) { $node->is_end = $is_end; } return $node; } // 查询子节点 public function searchChildNode($value) { foreach ($this->childNode as $k => $v) { if ($v->value == $value) { // 存在节点,返回该节点 return $this->childNode[$k]; } } return false; } } // 添加字符串 function addString(&$head, $str) { $node = null; for ($i = 0; $i < strlen($str); $i++) { if ($str[$i] != ' ') { $is_end = $i != (strlen($str) - 1) ? false : true; // 如果最后一位就是false; if ($i == 0) { $node = $head->addChildNode($str[$i], $is_end); } else { $node = $node->addChildNode($str[$i], $is_end); } } } } // 获取所有字符串--递归 function getChildString($node, $str_array = array(), $str = '') { if ($node->is_end == true) { $str_array[] = $str; } if (empty($node->childNode)) { return $str_array; } else { foreach ($node->childNode as $k => $v) { $str_array = getChildString($v, $str_array, $str . $v->value); } return $str_array; } } // 字符串多模匹配 function search($p, $head, &$failArray) { $i = 0; $res = []; while ($i < strlen($p)) { $head = searchWords($head, $p[$i], $res, $failArray, $i); $i++; } return $res; } function searchWords(&$head, $value, &$res, &$failArray, &$i) { foreach ($head->childNode as $k => $v) { if ($v->value == $value) { // 成功存入 if ($v->is_end == true) { $res[getWords($head->childNode[$k])][] = $i; } // fail节点也是指向一个结束节点 if ($failArray[$v->fail]->is_end == true) { $res[getWords($failArray[$v->fail])][] = $i; } // 跳转fail if (empty($v->childNode)) { return $failArray[$v->fail]; } // 继续下一级匹配 return $head->childNode[$k]; } } // fail指针正在后退,没到root节点主指针不动 if ($head->failIndex) { $i--; } // 失败指向fail 对比指针不动 return $failArray[$head->fail]; } // 获取完整字符 function getWords($node) { $str = ''; while ($node->parent) { $str .= $node->value; $node = $node->parent; } return strrev($str); } // 构造fail指针 function buildFailIndex(&$node, $fail_array = [], &$failTrie) { $fail_array[] = $node; if (!isset($failTrie[$node->trie])) { $failTrie[] = []; } ($failTrie[$node->trie])[] = &$node; $node->failIndex = count($fail_array) - 1; if (empty($node->childNode)) { return $fail_array; } else { foreach ($node->childNode as $k => $v) { $fail_array = buildFailIndex($node->childNode[$k], $fail_array, $failTrie); } return $fail_array; } } // 结构树 function trie(&$failArray, &$failTrie) { foreach ($failTrie as $k => $v) { // 层数循环 foreach ($v as $k1 => $v1) { // 每层每个节点循环 $failTrie[$k][$k1]->fail = buildTrie($failTrie[$k][$k1], $failArray); } } } function buildTrie(&$node, &$failArray) { if ($node->failIndex == 0 || $node->parent->failIndex === 0) { return 0; } // 循环问题 foreach ($failArray[$node->parent->fail]->childNode as $k => $v) { if ($v->value == $node->value) { return $v->failIndex; } } return 0; } /* 调用测试开始 */ $head = new Node; // 添加单词 addString($head, 'say'); addString($head, 'she'); addString($head, 'sher'); addString($head, 'h'); addString($head, 'her'); // fail二维指针数组(方便遍历) $failTrie = []; // 创建一个fail指针数组 $failArray = buildFailIndex($head, [], $failTrie); trie($failArray, $failTrie); var_dump(search('hshershrewr', $head, $failArray));
您的资助是我最大的动力!
金额随意,欢迎来赏!
![](https://images.cnblogs.com/cnblogs_com/GreenForestQuan/1540482/o_wx.png)