DFA算法与敏感词识别

6,174 阅读2分钟

DFA算法与敏感词识别

不得不说的字典树( Trie )

具有以下特征:

  • 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。
  • 每个单词的公共前缀作为一个字符节点保存。

总结:相对Hash,Trie占用空间少、查询效率高

DFA原理

DFA(Deterministic Finite Automaton,即确定有穷自动机。其原理为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。

举个例子:

root = { 
    isEnd = 0 
    王 = {
        isEnd = 0
        八 = {
            isEnd = 0 
            蛋 = {
                isEnd = 1
            } 
            羔 = {
                isEnd = 0
                子 = {
                    isEnd = 1
                }
            } 
        }
    }
}

运行逻辑:

  • 检测词,切割成一个个字:王、八、羔、子
  • 程序拿着 敏感词字典树找,有就返回 下面的子树,无则不是敏感词
  • 接上面 子树 程序开始匹配第二个字 ,有就返回 下面的子树,无则不是敏感词。以此类推...
  • isEnd = true,表明敏感词搜索已经结束,检测词不是敏感词。

程序实现

class DfaMap
{
    public function get($key)
    {
        return isset($this->$key) ? $this->$key : null;
    }

    public function put($key, $value)
    {
        $this->$key = $value;
    }
}

class DfaFilter
{
    public $map = null;

    public function addWordToMap($word)
    {
        $len = mb_strlen($word);
        if (is_null($this->map)) {
            $map = new DfaMap();
            $map->put('isEnd', 0);
        } else {
            $map = $this->map;
        }
        $tmp = $map;

        for ($i = 0; $i < $len; $i++) {
            $nowWord = mb_substr($word, $i, 1);

            $nowMap = $map->get($nowWord);

            if (!is_null($nowMap)) {
                $map = $nowMap;
            } else {
                $newMap = new DfaMap();
                $newMap->put('isEnd', 0);
                $map->put($nowWord, $newMap);
                $map = $newMap;
            }

            if ($i == ($len - 1)) {
                $map->put('isEnd', 1);
            }
        }
        $this->map = $tmp;
    }

    //仅支持最大匹配
    public function searchFromMap($string)
    {
        $len = mb_strlen($string);
        $tmp = $this->map;
        $map = $this->map;
        $str = '';
        $result = [];
        for ($i = 0; $i < $len; $i++) {
            $nowWord = mb_substr($string, $i, 1);
            $nowMap = $map->get($nowWord);

            if (!is_null($nowMap)) {
                $str .= $nowWord;
                if ($nowMap->get('isEnd')) {
                    array_push($result, $str);
                    $str = '';
                    $map = $tmp;
                } else {
                    $map = $nowMap;
                }
            } else {
                if (!empty($str)) {
                    $i--;
                }
                $str = '';
                $map = $tmp;
            }
        }
        return $result;
    }
}

$DfaFilter = new DfaFilter();
$DfaFilter->addWordToMap( '王羔子八' );
$DfaFilter->addWordToMap( '狗' );
var_dump($DfaFilter->searchFromMap('狗'));

硬伤

  • 敏感词量一上来,内存分分钟炸掉
  • 无意义词~!@#$%,拼音/字母干扰薇信/薇芯/∨信/∨芯/v送rmb


公司最近自己做敏感词识别。
我打算用ES了,@掘金 有无更好的玩法?

就比如这篇文章,就被掘金的“敏感词”屏蔽了😂😂😂
原创不易,如果觉得我的文章对你有帮助,请点赞鼓励