zoukankan      html  css  js  c++  java
  • 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://blog.csdn.net/weixin_33877092/article/details/91388544?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2

    您的资助是我最大的动力!
    金额随意,欢迎来赏!

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

    如果,想给予我更多的鼓励,求打

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读!

  • 相关阅读:
    【乱侃】How do they look them ?
    【softeware】Messy code,some bug of Youdao notebook in EN win7
    【随谈】designing the login page of our project
    【web】Ad in security code, making good use of resource
    SQL数据库内存设置篇
    关系数据库的查询优化策略
    利用SQL未公开的存储过程实现分页
    sql语句总结
    sql中使用cmd命令注销登录用户
    SQLServer 分页存储过程
  • 原文地址:https://www.cnblogs.com/GreenForestQuan/p/14352012.html
Copyright © 2011-2022 走看看