• 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

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

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

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

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

  • 相关阅读:
    cf854B Maxim Buys an Apartment
    Snuke's Coloring 2-1
    P1087 FBI树
    Card Game for Three
    Many Formulas
    排队
    苹果消消乐(尺取法)
    猴子选大王(约瑟夫)
    进制转化
    UIProgress控件的属性和方法
  • 原文地址:https://www.cnblogs.com/GreenForestQuan/p/14352012.html
Copyright © 2020-2023  润新知